LeetCode刷题之二叉树(遍历)

259 阅读5分钟

层次遍历

使用BFS遍历二叉树时,不需要使用两个队列来存储当前层节点和下一层节点,因为开始遍历的时候,当前层的节点数量就是队列的长度,只需要用这个来控制遍历当前层的节点即可,队列用来存储下一层的节点。

637. 二叉树的层平均值

给定一个非空二叉树, 返回一个由每层节点平均值组成的数组.

示例 1:

输入:
    3
   / \
  9  20
    /  \
   15   7
输出: [3, 14.5, 11]
解释:
第0层的平均值是 3,  第1层是 14.5, 第2层是 11. 因此返回 [3, 14.5, 11].

注意:

  • 节点值的范围在32位有符号整数范围内。

题解:这道题很明显是一道用BFS来解决的题目,用一个队列来存储下一层的节点,将每层的节点值累加起来,除于个数即可

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        //准备一个队列
        Queue<TreeNode> queue = new LinkedList<>();
        //先将根节点添加进去
        ((LinkedList<TreeNode>) queue).add(root);
        
        //BFS
        while (!queue.isEmpty()) {
            //用队列长度来控制每一层的个数
            int cnt = queue.size();
            double sum = 0;
            for (int i = 0; i < cnt; i++) {
                TreeNode node = queue.poll();
                sum += node.val;
                //将下一层节点添加进队列
                if (node.left != null) {
                    ((LinkedList<TreeNode>) queue).add(node.left);
                }
                if (node.right != null) {
                    ((LinkedList<TreeNode>) queue).add(node.right);
                }
            }
            result.add(sum / cnt);
        }
        return result;
    }
}

513. 找树左下角的值(Medium)

给定一个二叉树,在树的最后一行找到最左边的值。

示例 1:

输入:

    2
   / \
  1   3

输出:
1

示例 2:

输入:

        1
       / \
      2   3
     /   / \
    4   5   6
       /
      7

输出:
7

注意: 您可以假设树(即给定的根节点)不为 NULL。

解法一:这道题也是用BFS解决,使用队列从左至右存储下一层节点,保证每一层都是先遇到左边第一个节点,用变量res来存储这个节点,当遍历完队列时,返回res即可

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        //准备一个队列
        Queue<TreeNode> queue = new LinkedList<>();
        //先将根节点添加进去
        ((LinkedList<TreeNode>) queue).add(root);

        int res = 0;
        //BFS
        while (!queue.isEmpty()) {
            //用队列长度来控制每一层的个数
            int cnt = queue.size();
            for (int i = 0; i < cnt; i++) {
                TreeNode node = queue.poll();
                //将每一层的左边第一个节点传给res
                if (i == 0) {
                    res = node.val;
                }
                //将下一层节点添加进队列
                if (node.left != null) {
                    ((LinkedList<TreeNode>) queue).add(node.left);
                }
                if (node.right != null) {
                    ((LinkedList<TreeNode>) queue).add(node.right);
                }
            }
        }
        return res;
    }
}

解法二:这道题也可以使用“先序遍历”来解答

  • 先序遍历的顺序是:根->左->右,每一层的最左节点是首先遍历到的,
  • 当遍历到新一行的时候,当前深度肯定比之前的最大深度大,所以我们可以更新最大深度为当前深度,结点值res为当前结点值,这样在遍历到该行其他结点时就不会更新结果res了
class Solution {
    public int findBottomLeftValue(TreeNode root) {
        int res = root.val;
        int maxDepth = 1;
        helper(root, 1, maxDepth, res);
        return res;
    }

    private void helper(TreeNode root, int depth, int maxDepth, int res) {
        //base case
        if (root == null) {
            return;
        }
        //遍历到新一层的左边第一个节点
        if (depth > maxDepth) {
            maxDepth = depth;
            res = root.val;
        }
        //遍历左右节点
        helper(root.left, depth + 1, maxDepth, res);
        helper(root.right, depth + 1, maxDepth, res);
    }
}

前中后序遍历

层次遍历利用的是BFS,而前中后序利用的是DFS,用递归实现这三种遍历比较简单,而非递归比较难一点

递归实现前中后序遍历

  1. 先序遍历
    public void dfs(TreeNode root) {
        //base case
        if (root == null) {
            return;
        }
    
        //先访问根节点
        System.out.println(root.val);
        //遍历左节点和右节点
        dfs(root.left);
        dfs(root.right);
    }
    
  2. 中序遍历
    public void dfs(TreeNode root) {
        //base case
        if (root == null) {
            return;
        }
    
        //先遍历左节点
        dfs(root.left);
        //中间访问根节点
        System.out.println(root.val);
        //再遍历右节点
        dfs(root.right);
    }
    
  3. 后续遍历
    public void dfs(TreeNode root) {
        //base case
        if (root == null) {
            return;
        }
    
        //先遍历左节点
        dfs(root.left);
        //再遍历右节点
        dfs(root.right);
        //最后访问根节点
        System.out.println(root.val);
    }
    

非递归实现前中后序遍历

144. 二叉树的前序遍历(Medium)

题解

  • 先序遍历的特点是:对访问的每个节点,先访问根节点,再访问左节点,最后访问右节点
  • 递归实现我们借助的是“栈”,只不过这个“栈”不是我们自己实现的,现在就需要我们自己实现
  • 准备一个栈,压入节点的时候要注意“先压右节点”,因为弹出来的时候是相反的顺序
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null) {
            return result;
        }

        //准备一个栈
        Stack<TreeNode> stack = new Stack<>();
        //先将根节点压入
        stack.push(root);
        //先序遍历二叉树
        while (!stack.isEmpty()) {
            //先访问根节点
            TreeNode node = stack.pop();
            //遇到空节点,跳过
            if (node == null) {
                continue;
            }
            result.add(node.val);
            //如果有左右节点,就压入
            if (node.right != null) {
                stack.push(node.right);
            }
            if (node.left != null) {
                stack.push(node.left);
            }
        }
        return result;
    }
}

145. 二叉树的后序遍历(Hard)

题解:前序遍历为 root -> left -> right,后序遍历为 left -> right -> root。可以修改前序遍历成为 root -> right -> left,那么这个顺序就和后序遍历正好相反。

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        //利用先序遍历遍历节点
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            if (node == null) continue;
            ret.add(node.val);
            stack.push(node.left);
            stack.push(node.right);
        }
        //最后将结果反转即可
        Collections.reverse(ret);
        return ret;
    }
}

94. 二叉树的中序遍历(Medium)

题解:中序遍历也需要用到栈,不同的是遍历条件多了个cur != null,因为中序遍历是先一直将左节点压入栈,遍历到为空之后就弹出,加入list,然后往右遍历

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        if (root == null) {
            return ret;
        }
        //准备一个栈
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (!stack.isEmpty() || cur != null) {
            //将左节点全部压入(左)
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            //遇到空,弹出节点(根),然后往右走
            TreeNode node = stack.pop();
            ret.add(node.val);
            //右
            cur = node.right;
        }
        return ret;
    }
}