二叉树的深度遍历

人的大脑真是很强大的器官,可以帮我们潜意识的处理很多事情,让我们会觉得好多“运行”都很简单。

就比如二叉树的遍历,我们以先序遍历(Preorder Traversal)举例:

如果我们人脑先序遍历上面这个二叉树,我们会很容易的说出:FBADCEGIE,这样正确的结果,人脑帮我们“存储”了很多细节,所以对于我们人脑很容易。但是,对于计算机就没那么容易,计算机得明确的存储细节。

我们人脑遍历时,是这样的,先知道F要输入,然后左边B输出,再左边A输入,左边没有了,那就是D了,那么问题来了,人脑是怎么找到D的呢?这是因为我们记住了(又看了一眼)B的左节点是A,右节点是D。重点来了,计算机也是需要“记住的”,这个对于计算机来说也简单,就是用个数组存下来。

我们知道对于这种遍历可以用递归(Recursive)很简单的实现,这个大家都知道,那么能不能不用递归用别的方式,比如迭代(iterative)实现了呢?答案是肯定的。

我理解,递归只是从代码程度简化了,其实本质上内部实现还是迭代。

不过迭代实现遍历也又两种方式,一种按照递归的方式实现,另一种是根据先序遍历的特点用更巧妙的方式实现,具体参考下面的问题。

在leetcode种就有,用迭代的方式实现二叉树的先序遍历,java实现代码如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList();
        if (root != null) {
            Stack<TreeNode> stack = new Stack();
            stack.push(root);
            while(!stack.empty()) {
                TreeNode topNode = stack.pop();
                result.add(topNode.val);
                if(topNode.right != null) {
                    stack.push(topNode.right);
                }
                if(topNode.left != null) {
                    stack.push(topNode.left);
                }
            }
        }
        return result;
    }
}

中序遍历非递归实现,这个按照递归的步骤实现就行。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList();
        if (root != null) {
            Stack<TreeNode> stack = new Stack();
            TreeNode tmp = root;
            while(tmp != null ||!stack.empty()) {
                while (tmp != null) {
                    stack.push(tmp);
                    tmp = tmp.left;  
                }  
                tmp = stack.pop();
                result.add(tmp.val);
                tmp = tmp.right;  
            }
        }
        return result;
    }
}

比较难的是后序遍历的非递归实现,用递归的步骤实现,应该也不是不能实现,当时决定会很麻烦。简单的实现,就用到了纯智慧的方法了,这就是需要记忆的。

后序遍历的结果是先序遍历左右树遍历顺序交互的结果,其实也能理解,先序遍历是先访问根的,后序遍历是最后访问根的,所以是有关系的。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList();
        if (root != null) {
            Stack<TreeNode> stack = new Stack();
            stack.push(root);
            while (!stack.empty()) {
                TreeNode node = stack.pop();
                result.add(node.val);
                if(node.left != null) {
                    stack.push(node.left);
                }
                if (node.right != null) {
                    stack.push(node.right);
                }
            }
        }
        List<Integer> finalResult = new ArrayList();
        for(int i = result.size() - 1; i >= 0; i--) {
            finalResult.add(result.get(i));
        }
        return finalResult;
    }
}

这三种遍历是深度遍历,是用栈stack实现的,stack是FILO(first in last out)先进后出的。

思考一个问题,为啥有递归这种简单的方式完成遍历,为啥还有寻求非递归的方式遍历呢?岂不是没事找事吗?

leetcode给出来答案

As we mentioned before, we can traverse a tree recursively to retrieve all the data in pre-order, in-order or post-order. The time complexity is O(N) because we visit each node exactly once. And the depth of the tree might be N in the worst case. That is to say, the level of recursion might be at most N in the worst case. Therefore, taking system stack into consideration, the space complexity is O(N) as well.

每个节点只访问一次,所以时间复杂度是O(N)。一个有N个node的树,在最坏的情况下,树的深度是N。递归也是需要不断入栈的,只不过是系统维护的栈。最坏的情况下需要递归N次(想像一下,全部是左树),这样空间复杂度就是O(N),因为每个node都得入栈。最好的情况下,是平衡二叉树,这样深度就是log(N),底数是2可不是10,这样空间复杂度就是O(log(N))。

To be cautious, the complexity might be different due to a different implementation. It is comparatively easy to do traversal recursively but when the depth of the tree is too large, we might suffer from stack overflow problem. That's one of the main reasons why we want to solve this problem iteratively sometimes.

使用递归遍历是相当容易的,但是如果树的深度很大时,我们可能会遇到栈溢出的问题。这就是为啥我们想采用迭代的方式去遍历。

For the iterative solution, the time complexity is apparently the same with recursion solution which is O(N). The space complexity is also O(N) since in the worst case, we will have all the nodes in the stack. There are some other solutions for iterative traversal which can reduce the space complexity to O(1).

 

这里面用到了java的stack类,常用方法用push、pop、peek、empty

 

参考:

递归和迭代的区别

二叉树的先序遍历-递归和非递归算法

二叉树的中序遍历-递归和非递归算法

二叉树的后序遍历-递归和非递归算法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值