每日刷题:第三十天 二叉树的迭代遍历方法

虽然使用递归可以很简单的解决深度优先遍历问题,但是递归函数对于栈的占用太大了,很容易就造成了栈溢出。那么有没有其他方式来解决遍历问题呢?

答案是肯定的,那就是今天学习到的迭代遍历方法

为了便于记忆,我这里使用的是统一的迭代方法(利用栈获取正确的顺序),它有以下规则

①入栈顺序要与出栈顺序相反(例如:前序遍历是:中左右出栈,那么入栈就是右左中)

②在要出栈的结点之后入一个空结点作为标记(后面会讲原因)

③使用根节点获取正确的入栈顺序(意思就是在每一次循环中,对于当前结点都视为根节点)

④对于空的节点,不入栈处理(与②相关,我们已经将空结点作为标记使用了,那么对于空结点直接不处理即可)

大家可以先按自己想法,利用迭代的方式写一下前序遍历,再尝试将前序遍历改为中序遍历,你会发现一个难点:前序遍历是直接出根节点,这很好处理,但对于中序遍历,要先出左节点,而获取数据又要从根节点中获取,也就是说根节点一定要先入栈。这个就是迭代的难点。

那么我们根据以上规则,写一个前序遍历出来,来比较一下它的优点

 public List preorderTraversal(TreeNode root) {
        //创建结果数组
        List result = new ArrayList<>();
        //建立栈获取正确的顺序
        Stack stack = new Stack<>();
        //先放入根节点(要求不为空)
        if(root != null) stack.push(root);
        //结束条件:栈不为空
        while(!stack.empty())
        {
            //若不是空节点,则将其看做根节点
            if (stack.peek() != null)
            {
                //因为这个根节点是拿来获取数值的,所以将其弹出
                //如果不弹出的话,会影响出栈顺序 
                TreeNode root2 = stack.pop();
                //根据正确顺序入栈
                if (root2.right != null) stack.push(root2.right);
                if (root2.left != null) stack.push(root2.left);
                //中间结点无需判空(原因看上面的代码)
                stack.push(root2);
                //在要输出的结点之后打上null标记
                stack.push(null);
                
            }else//如果为空节点,那么说明空节点的下一个节点是要输出到结果数组中的
            {
                //先弹出空节点
                stack.pop();
                //弹出下一个节点放入结果数组中
                result.add(stack.pop().val);
            }
        }
        return result;
    }

我们获取数据仍然从根节点处获得,但具体操作可以看到,我们每次进来获取根节点的同时都直接弹出了根节点,这样子既获得了数据,又不影响正确的输出,第二点我们通过标记去输出结点,这样子的统一规则之下,前中后序遍历只需修改几行代码的顺序即可

中序:

 public List inorderTraversal(TreeNode root) {
        //创建结果数组
        List result = new ArrayList<>();
        //建立栈获取正确的顺序
        Stack stack = new Stack<>();
        //先放入根节点
        if(root != null) stack.push(root);
        //结束条件:栈不为空
        while(!stack.empty())
        {
            //若不是空节点,则将其看做根节点
            if (stack.peek() != null)
            {
                //因为这个根节点是拿来获取数值的,所以将其弹出
                //如果不弹出的话,会影响出栈顺序
                TreeNode root2 = stack.pop();
                //根据正确顺序入栈
                if (root2.right != null) stack.push(root2.right);
                //中间结点无需判空(原因看上面的代码)
                stack.push(root2);
                //在要输出的结点之后打上null标记
                stack.push(null);
                if (root2.left != null) stack.push(root2.left);

            }else//如果为空节点,那么说明空节点的下一个节点是要输出到结果数组中的
            {
                //先弹出空节点
                stack.pop();
                //弹出下一个节点放入结果数组中
                result.add(stack.pop().val);
            }
        }
        return result;
    }

后序:

  public List postorderTraversal(TreeNode root) {
        //创建结果数组
        List result = new ArrayList<>();
        //建立栈获取正确的顺序
        Stack stack = new Stack<>();
        //先放入根节点
        if(root != null) stack.push(root);
        //结束条件:栈不为空
        while(!stack.empty())
        {
            //若不是空节点,则将其看做根节点
            if (stack.peek() != null)
            {
                //后序遍历可以简单优化一下,因为中间结点本来就第一个入栈,所以就不用出栈了
                TreeNode root2 = stack.peek();
                //中间结点无需判空(原因看上面的代码)
                //在要输出的结点之后打上null标记
                stack.push(null);
                //根据正确顺序入栈
                if (root2.right != null) stack.push(root2.right);
                if (root2.left != null) stack.push(root2.left);

            }else//如果为空节点,那么说明空节点的下一个节点是要输出到结果数组中的
            {
                //先弹出空节点
                stack.pop();
                //弹出下一个节点放入结果数组中
                result.add(stack.pop().val);
            }
        }
        return result;
    }

你可能感兴趣的:(力扣刷题日记,leetcode)