二叉树的四种遍历方式(递归和非递归)

这篇文章的目的只是整理几个常用的二叉树递归/非递归遍历的模板函数,方便用到的时候凭借记忆写出来即可。内容没多少技术含量,写给自己看的,权当参考。

前序遍历

递归:很简单,按照根左右的逻辑访问就是。

public List<Integer> preorderTraversal(TreeNode root){
        List<Integer> res = new ArrayList<Integer>();
        inorder(root, res);
        return res;
    }

    public void preorder(TreeNode root, List<Integer> res){
        if(root == null) return;
        res.add(root.val);
        preorder(root.left, res);
        preorder(root.right, res);
    }

在所有树状结构的数据结构当中,递归真的是一种最最省事的写法了。如果题目没有要求,建议直接递归就vans了。
非递归(这才是本篇文章的重点):用一个栈,前序遍历的非递归是三种非递归当中最好理解的一种了,先把根节点入栈,再遵循当前节点弹栈->当前节点左节点入栈->当前节点右节点入栈即可。

public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        if(root != null){
            stack.push(root);
        }
        TreeNode temp;
        while(!stack.isEmpty()){
            temp = stack.pop();
            res.add(temp.val);
            if(temp.right != null){
                stack.push(temp.right);
            }
            if(temp.left != null){
                stack.push(temp.left);
            }
        }
        return res;
    }

中序遍历

递归:还是很简单,左根右结构的访问即可。

	public List<Integer> inorderTraversal(TreeNode root){
        List<Integer> res = new ArrayList<Integer>();
        inorder(root, res);
        return res;
    }

    public void inorder(TreeNode root, List<Integer> res){
        if(root == null) return;
        inorder(root.left, res);
        res.add(root.val);
        inorder(root.right, res);
    }

非递归:使用一个栈,因为是左根右结构的遍历,所以看到当前节点有左节点就先把它加入栈,直到没有左节点了,再弹栈当前节点,如果当前节点有右子树的话就切换到右子树上即可。

	public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode temp = root; //temp变量只在入栈预处理阶段使用
        TreeNode cur;
        while(temp != null || !stack.isEmpty()){
        	// 只要有左节点,就压栈
            if(temp != null){
                stack.push(temp);
                temp = temp.left;
            }else{
            	// 没有左节点了,访问当前节点
                cur = stack.pop();
                res.add(cur.val);
                // 有右子树的话就切换到右子树上
                if(cur.right != null){
                    temp = cur.right;
                }
            }
        }
        return res;
    }

后序遍历

递归:……(省略)

	public List<Integer> inorderTraversal(TreeNode root){
        List<Integer> res = new ArrayList<Integer>();
        inorder(root, res);
        return res;
    }

    public void inorder(TreeNode root, List<Integer> res){
        if(root == null) return;
        inorder(root.left, res);
        inorder(root.right, res);
        res.add(root.val);
    }

非递归:也是使用一个栈,这里和中序遍历的非递归不同之处在于,要用一个指针last记录下上一个节点,如果是从last过来的,就直接遍历当前节点结束(因为此时已经走完了一轮左->右->根的流程),如果不是就切换到右子树上去。

public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode temp = root; //temp变量只在入栈预处理阶段使用
        TreeNode cur;
        TreeNode last = null; // last和cur只在出栈遍历阶段使用
        while(temp != null || !stack.isEmpty()){
        	// 只要有左节点,就压栈
            if(temp != null){
                stack.push(temp);
                temp = temp.left;
            }else{
            	// 确定是否切换到右子树
            	// 如果有右节点,又不是刚刚从右子树过来的,就切换到右子树
                cur = stack.peek();
                if(cur.right != null && cur.right != last){
                    temp = cur.right;
                }else{
                	// 左右都遍历完了,遍历当前节点并弹栈
                	// 记录last节点
                    res.add(cur.val);
                    stack.pop();
                    last = cur;
                }
            }
        }
        return res;
    }

层序遍历

这个一般人一开始没法想到递归了。但是也很简单,用一个队列保存节点即可。这个顺序很好理解,遍历到一个节点的时候,就把它的左节点->右节点加入队列,只要队列不空,就一直访问下去。
还有一个问题是如何记录层的信息,即如何把每一层的节点分开的问题。只要在每一层开始的时候,用queue.size()记录下队列中当前的节点数,这就是当前这一层的所有节点了。然后用一个变量保存遍历过的节点数,只要这个变量与size()相等了,说明当前这一层遍历完了,清空变量再用size()得到下一层的节点数,如此往复即可。

	public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> levels = new ArrayList<List<Integer>>();
        if (root == null) return levels;
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        int level = 0;
        while ( !queue.isEmpty() ) {
            levels.add(new ArrayList<Integer>());
            int level_length = queue.size(); //记录当前层的长度
            // 在当前层里记录每一个节点,同时把下一层的节点加入队列
            for(int i = 0; i < level_length; ++i) {
                TreeNode node = queue.poll();
                levels.get(level).add(node.val);
                if (node.left != null) queue.offer(node.left);
                if (node.right != null) queue.offer(node.right);
            }
        level++;
        }
        return levels;
    }

但是层序遍历其实也可以通过递归来求解的,下面贴出递归的代码:

List<List<Integer>> levels = new ArrayList<List<Integer>>();

    public void helper(TreeNode node, int level) {
        // start the current level
        if (levels.size() == level)
            levels.add(new ArrayList<Integer>());

         // fulfil the current level
         levels.get(level).add(node.val);

         // process child nodes for the next level
         if (node.left != null)
            helper(node.left, level + 1);
         if (node.right != null)
            helper(node.right, level + 1);
    }
    
    public List<List<Integer>> levelOrder(TreeNode root) {
        if (root == null) return levels;
        helper(root, 0);
        return levels;
    }

以上层序遍历的两种解法均来自leetcode官方题解,递归解法用得少,只需要掌握非递归解法即可。
其实上面说到的非递归解法中,前中后序遍历的三种解法可以视作是dfs,而最后一种层序遍历则可视作是bfs。这也启发我们想到,dfs相关的思想(甚至是回溯相关的思想)可以用栈来解决,而bfs可以用队列来解决。在不用递归的情况下,做出递归的效果,一般依靠的都是这两种数据结构。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值