二叉树的遍历算法(DFS和BFS)

/* Definition for a binary tree node. */
public class TreeNode {
  int val;
  TreeNode left;
  TreeNode right;

  TreeNode(int x) {
    val = x;
  }
}

深度优先遍历(DFS)

迭代算法

前序遍历(先访问根节点)

用一个stack进行辅助

class Solution {
  public List<Integer> preorderTraversal(TreeNode root) {
    Deque<TreeNode> stack = new ArrayDeque<>();
    LinkedList<Integer> res = new LinkedList<>();
    if (root == null) {
      return res;
    }

    stack.push(root);
    while (!stack.isEmpty()) {
      TreeNode node = stack.pop();
      res.add(node.val);
      if (node.right != null) {
        stack.push(node.right);
      }
      if (node.left != null) {
        stack.push(node.left);
      }
    }
    return res;
  }
}

中序遍历(中间访问根节点)

用一个stack进行辅助

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
            List<Integer> res = new LinkedList<>();
            Deque<TreeNode> stack = new ArrayDeque<>();
            if(root == null)
                    return res;
            TreeNode curr = root;
            while(curr!=null || !stack.isEmpty()){
                    while(curr != null){
                            stack.push(curr);
                            curr = curr.left;
                    }
                    curr = stack.pop();
                    res.add(curr.val);
                    curr = curr.right;
            }
            return res;
    }
}

后序遍历(最后访问根节点)

后序遍历相较于前两种更复杂一些,有两种方法。

方法一:

用一个stack进行辅助,mark标记了上一个访问过的节点,在判断节点是否入栈时将mark作为判断条件之一,这样就避免了访问过的节点再次入栈。

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
            LinkedList<Integer> res = new LinkedList<>();
            Deque<TreeNode> stack = new ArrayDeque<>();
            if(root == null)
                    return res;
            stack.push(root);
            TreeNode mark = root;
            while(!stack.isEmpty()){
                    TreeNode top = stack.peek();
                    if(top.left != null && top.left != mark && top.right != mark){
                            stack.push(top.left);
                    }else if(top.right != null && top.right != mark){
                            stack.push(top.right);
                    }else{
                            mark = stack.pop();
                            res.add(mark.val);
                    }
                    
            }
            return res;
    }
}
方法二:

用了两个stack进行辅助,思路是将待访问的节点按照逆序push进stack2,然后遍历stack2的顺序即为后续遍历的顺序。之所以能够这么做是因为我们最先访问的节点是父节点,这个节点在后续遍历中最后访问,我们就可以将它最先push进stack2,然后再push右子节点、左子节点。在push右子节点时,它本身也是一个父子节点,所以再push它的右子节点、左子节点。这种递归关系被保存在stack1中,使得节点按照顺序push进stack2中。

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
            LinkedList<Integer> res = new LinkedList<>();
            if(root == null)
                    return res;
            Deque<TreeNode> stack1 = new ArrayDeque<>();
            Deque<TreeNode> stack2 = new ArrayDeque<>();
            stack1.push(root);
            while(!stack1.isEmpty()){
                    TreeNode temp = stack1.pop();
                    stack2.push(temp);
                    if(temp.left != null)
                            stack1.push(temp.left);
                    if(temp.right != null)
                            stack1.push(temp.right);  
            }
            while(!stack2.isEmpty()){
                    res.add(stack2.pop().val);
            }
            return res;
    }
}

递归算法

递归算法比较简单,这里不再赘述

迭代算法与递归算法比较

因为每一个节点都要被访问,所以两种方法的时间复杂度都为O(N)。对于空间复杂度,递归算法的空间复杂度为O(N),当树很大时,会出现stackoverflow的问题。迭代算法由于借助了stack,最坏的情况下节点全部入栈,因此空间复杂的也为O(N),同样存在stackoverflow的问题。
值得注意的是,有一种迭代算法可以将空间复杂度降低到O(1),它就是Morris迭代算法

广度优先遍历(BFS)

递归算法

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
            List<List<Integer>> res = new ArrayList<>();
            if(root == null)
                return res;
            helper(root, 0, res);
            return res;
    }
        public void helper(TreeNode node, int level, List<List<Integer>> res){
                if(res.size() == level)
                        //如果没有这一层,则创建它
                        res.add(new ArrayList<Integer>());
                res.get(level).add(node.val);
                if(node.left != null)
                        helper(node.left, level+1, res);
                if(node.right != null)
                        helper(node.right, level+1, res);
        }
}

迭代算法

每次迭代都创建新的一层,把这层上的节点(被储存在queue中)的值放进去,并按从左到右的顺序把每个节点的左右子节点放进queue,作为下一层的节点储存下来。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
            List<List<Integer>> res = new ArrayList<>();
            if(root == null)
                    return res;
            Queue<TreeNode> queue = new LinkedList<>();
            queue.add(root);
            int level = 0;
            while(!queue.isEmpty()){
                    res.add(new ArrayList<Integer>());
                    int queueSize = queue.size();
                    for(int i = 0; i<queueSize; i++){
                            TreeNode temp = queue.remove();
                            res.get(level).add(temp.val);
                            if(temp.left != null)
                                    queue.add(temp.left);
                            if(temp.right != null)
                                    queue.add(temp.right);
                            
                    }
                    level++;
            }
            
            return res;
        }
}

迭代算法与递归算法比较

两种算法的时间复杂度和空间复杂度都为O(N)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值