当前位置: 首页 > article >正文

Leetcode 662: 二叉树最大宽度

Leetcode 662: 二叉树最大宽度 是一道考察树的层序遍历和位编号思想的经典题目。其目标是找到二叉树的每一层的最大宽度,宽度定义为:最左节点和最右节点间的节点数(包含这两个节点,中间可以有空节点)


题目描述

  • 给定一个二叉树的根节点 root,返回其最大宽度
  • “宽度”定义为:
    • 每层最左节点(即层级中最左非空节点)到最右节点之间节点的数目。
    • 如果同一层只有一个非空节点,则宽度为 1。

解法 1:层序遍历 + 队列 (广度优先搜索 BFS)

思路
  • 维护位置信息
    • 使用广度优先遍历 BFS,同时为每个节点分配一个基于完全二叉树的编号。根节点的编号为 1,左孩子为 2 * x,右孩子为 2 * x + 1
    • 每层的宽度可通过计算该层最右节点和最左节点对应编号的差值 width = right - left + 1
  • 层序遍历逐层更新最大宽度
    • 使用一个队列,存储每层节点和它们的编号 (node, index)
    • 遍历每一层时,记录最左节点和最右节点对应的编号,然后计算宽度,更新最大值。

代码模板
class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        if (root == null) return 0;

        // 队列存节点和它的"编号" (1-based index for position)
        Deque<Pair<TreeNode, Integer>> queue = new ArrayDeque<>();
        queue.offer(new Pair<>(root, 1));
        int maxWidth = 0;

        while (!queue.isEmpty()) {
            int size = queue.size();
            int left = queue.peekFirst().getValue(); // 当前层最左的编号
            int right = queue.peekLast().getValue(); // 当前层最右的编号
            maxWidth = Math.max(maxWidth, right - left + 1);

            for (int i = 0; i < size; i++) {
                Pair<TreeNode, Integer> pair = queue.poll();
                TreeNode node = pair.getKey();
                int index = pair.getValue();

                // 添加左右孩子到队列
                if (node.left != null) {
                    queue.offer(new Pair<>(node.left, 2 * index));
                }
                if (node.right != null) {
                    queue.offer(new Pair<>(node.right, 2 * index + 1));
                }
            }
        }

        return maxWidth;
    }
}

复杂度分析
  • 时间复杂度:O(n)
    • 每个节点入队和出队各一次,总的时间复杂度为 O(n)。
  • 空间复杂度:O(n)
    • 最差情况下,队列存储所有节点 (如完全二叉树的某一层)。

解法 2:DFS 深度优先搜索 + 位编号

思路
  • 递归方法:
    • 对每个节点,按照广度优先编号规则计算其位置索引,然后递归处理左、右子树。
    • 使用一个 Map<Integer, Integer> 记录每层的最左节点编号。
    • 对每个节点,计算当前宽度:width = currentIndex - leftIndexAtCurrentLevel + 1
    • 同时在递归过程中维护最大宽度值。

关键步骤
  1. 递归传递 当前节点的编号层级信息
  2. 递归更新宽度
    • 如果是当前层第一个访问的节点,记录它的编号为该层的最左编号。
    • 更新当前层宽度,调整全局最大宽度。
  3. 对左右子树分别递归处理:
    • 左子树位置编号为 2 * currentIndex
    • 右子树位置编号为 2 * currentIndex + 1

代码模板
class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        Map<Integer, Integer> leftMostPos = new HashMap<>(); // 每层最左节点的编号
        return dfs(root, 0, 1, leftMostPos);
    }

    private int dfs(TreeNode node, int depth, int index, Map<Integer, Integer> leftMostPos) {
        if (node == null) return 0;

        // 如果当前层是第一次访问,记录最左位置
        leftMostPos.computeIfAbsent(depth, x -> index);

        // 当前宽度 = 当前节点的编号 - 当前层最左编号 + 1
        int currentWidth = index - leftMostPos.get(depth) + 1;

        // 递归求左右子树的最大宽度
        int leftWidth = dfs(node.left, depth + 1, 2 * index, leftMostPos);
        int rightWidth = dfs(node.right, depth + 1, 2 * index + 1, leftMostPos);

        // 返回当前层和左右子树的最大宽度
        return Math.max(currentWidth, Math.max(leftWidth, rightWidth));
    }
}

复杂度分析
  • 时间复杂度:O(n)
    • 每个节点只访问一次。
  • 空间复杂度:O(h)
    • 递归调用栈的最大深度为树的高度,最差情况为 O(n)(如链状树)。

解法 3:BFS 优化(直接计算宽度)

优化点

相比解法 1 的队列实现,这里省略使用 Pair 来记录编号数,直接在每层中计算 startend 的编号即可。


代码模板
class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        if (root == null) return 0;
        Deque<TreeNode> nodeQueue = new ArrayDeque<>();
        Deque<Integer> indexQueue = new ArrayDeque<>();
        nodeQueue.offer(root);
        indexQueue.offer(1);
        int maxWidth = 0;

        while (!nodeQueue.isEmpty()) {
            int size = nodeQueue.size();
            int startIndex = indexQueue.peekFirst();
            int endIndex = indexQueue.peekLast();
            maxWidth = Math.max(maxWidth, endIndex - startIndex + 1);

            for (int i = 0; i < size; i++) {
                TreeNode currNode = nodeQueue.poll();
                int currIndex = indexQueue.poll();

                if (currNode.left != null) {
                    nodeQueue.offer(currNode.left);
                    indexQueue.offer(2 * currIndex);
                }

                if (currNode.right != null) {
                    nodeQueue.offer(currNode.right);
                    indexQueue.offer(2 * currIndex + 1);
                }
            }
        }

        return maxWidth;
    }
}

复杂度分析
  • 时间复杂度:O(n)
    • 每个节点入队、出队各一次。
  • 空间复杂度:O(n)
    • 队列需要存储某一层的所有节点。

快速AC策略

  1. 选择解法:
    • 如果面试中强调代码可读性:写BFS 队列解法(解法 1)。清晰易懂,是优先选择。
    • 如果数据规模中等且需要最优效率:写DFS 解法(解法 2)。递归代码更简洁,但需注意递归深度。
  2. 注意特殊情况
    • 输入为 null 的边界。
    • 确保编号的范围不会超过 int 取值范围(通常树的层级不会超过几十层)。
  3. 模板熟练度
    • BFS 模板:尽量减少额外的数据结构操作(比如 Pair)。
    • DFS 模板:确保层编号和位编号一致处理。

通过以上解法分析与模板准备,可以快速完成这道题并 AC!


http://www.kler.cn/a/570081.html

相关文章:

  • 【江科协-STM32】6. TIM编码器接口
  • Oracle数据库安全防护体系构建与核心技术解析
  • Artec Leo+Ray II 三维扫描仪成功为VR展数字化30吨重设备-沪敖3D
  • 网络安全架构三明治
  • 水仙花数(华为OD)
  • 最新版AI大模型面试八股文
  • 基于Asp.net的高校社交学习交流平台
  • 表关联查询:utf8mb4_general_ci和utf8mb4_0900_ai_ci两种不同的校对规则在操作符‘=‘时混用了
  • DeepSeek掘金——DeepSeek-R1驱动的房地产AI代理
  • OLMo OCR:让文字从图片里“跳”出来的魔法工具
  • 本地部署大数据集群前置准备
  • 多任务学习MTL+多任务损失
  • 机械视觉组成模块-相机选型
  • 从零开始用react + tailwindcss + express + mongodb实现一个聊天程序(九) 消息接口
  • 【含文档+PPT+源码】基于SpringBoot电脑DIY装机教程网站的设计与实现
  • 面试常问的压力测试问题
  • 实时金融信息搜索的新突破:基于大型语言模型的智能代理框架
  • 前端项目打包生成 JS 文件的核心步骤
  • OpenCV计算摄影学(12)色调映射(Tone Mapping)的一个类cv::TonemapMantiuk
  • CELLO : Causal Evaluation of Large Vision-Language Models