leetcode
leetcode 501 ~ 550
二叉树的边界

二叉树的边界

难度:

标签:

题目描述

代码结果

运行时间: 27 ms, 内存: 17.5 MB


/*
 * 题目思路:
 * 1. 使用流处理来实现二叉树的边界遍历。
 * 2. 找到左边界、叶子节点、右边界,并合并成结果。
 */
 
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}
 
public class BoundaryOfBinaryTreeStream {
    public List<Integer> boundaryOfBinaryTree(TreeNode root) {
        if (root == null) return Collections.emptyList();
        List<Integer> leftBoundary = new ArrayList<>();
        List<Integer> rightBoundary = new ArrayList<>();
        List<Integer> leaves = new ArrayList<>();
        if (!isLeaf(root)) leftBoundary.add(root.val);
        collectLeftBoundary(root.left, leftBoundary);
        collectLeaves(root, leaves);
        collectRightBoundary(root.right, rightBoundary);
        Collections.reverse(rightBoundary);
        return Stream.of(leftBoundary, leaves, rightBoundary)
                     .flatMap(Collection::stream)
                     .collect(Collectors.toList());
    }
 
    private void collectLeftBoundary(TreeNode node, List<Integer> res) {
        while (node != null) {
            if (!isLeaf(node)) res.add(node.val);
            if (node.left != null) node = node.left;
            else node = node.right;
        }
    }
 
    private void collectLeaves(TreeNode node, List<Integer> res) {
        if (isLeaf(node)) {
            res.add(node.val);
            return;
        }
        if (node.left != null) collectLeaves(node.left, res);
        if (node.right != null) collectLeaves(node.right, res);
    }
 
    private void collectRightBoundary(TreeNode node, List<Integer> res) {
        while (node != null) {
            if (!isLeaf(node)) res.add(node.val);
            if (node.right != null) node = node.right;
            else node = node.left;
        }
    }
 
    private boolean isLeaf(TreeNode node) {
        return node.left == null && node.right == null;
    }
}

解释

方法:

这个题目可以通过深度优先搜索(DFS)来解决。解题思路如下: 1. 先将根节点的值加入结果列表。 2. 通过先序遍历(preorder)得到左边界节点的值,并加入结果列表。注意要排除叶子节点。 3. 通过DFS找到所有叶子节点的值,按从左到右的顺序加入结果列表。 4. 通过后序遍历(postorder)得到右边界节点的值,并以逆序的方式加入结果列表。注意要排除叶子节点。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在定义左边界和右边界的节点时,为什么选择排除叶子节点而不是包括它们在内?
在构造二叉树边界的过程中,如果在左边界和右边界中包含叶子节点,那么当这些叶子节点同时被计算为底部边界时,它们会在结果列表中被重复计算。为了避免这种重复,我们在左边界和右边界的计算中排除叶子节点,只在专门寻找叶子节点的步骤中将它们包括进来,确保每个节点在最终结果中只出现一次。
🦆
在进行左边界的先序遍历时,如果遇到一个节点既没有左子树也没有右子树时,函数直接返回,这是否意味着可能会漏掉一些边界条件?
在左边界的定义中,一个没有左子树也没有右子树的节点即为叶子节点,因此在定义左边界时会跳过这样的节点。这种设计是故意的,以避免叶子节点在左边界或右边界中被重复计算。因此,这并不会漏掉边界条件,而是为了保持结果的一致性与准确性。
🦆
当根节点只有一侧子树(例如只有左子树或只有右子树)时,这种情况下的边界处理逻辑是否有所不同?
如果根节点只有一侧子树,处理逻辑会略有不同。例如,如果只有左子树而无右子树,左边界就是从根节点开始直到最左侧的路径,而右边界不存在。在这种情况下,右边界的递归函数不会有任何执行,因此只需要确保左边界和叶子节点正确收集。如果只有右子树,情况相反,需要正确处理右边界而忽略左边界。
🦆
在递归函数`leftBoundary`和`rightBoundary`中,如果节点的左或右子树为null,你选择遍历另一侧子树。这种选择是否可能影响到最终边界的正确性?
在这种设计中,如果一个节点的一侧子树为null,选择遍历另一侧子树是为了确保边界的完整性。这种方法确保了在没有传统边界(如左子树或右子树)的情况下仍能尽可能地追踪边界。虽然这样做可能会导致边界看起来不是很直观(例如,边界可能包含了通常不认为是边界的节点),但这是为了保持边界的连续性,并确保所有外围的节点被考虑在内。

相关问题

二叉树的右视图

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

 

示例 1:

输入: [1,2,3,null,5,null,4]
输出: [1,3,4]

示例 2:

输入: [1,null,3]
输出: [1,3]

示例 3:

输入: []
输出: []

 

提示:

  • 二叉树的节点个数的范围是 [0,100]
  • -100 <= Node.val <= 100