首页 > 其他分享 >图解LeetCode——662. 二叉树最大宽度(难度:中等)

图解LeetCode——662. 二叉树最大宽度(难度:中等)

时间:2023-05-23 11:06:11浏览次数:36  
标签:node level 662 节点 二叉树 编号 null root LeetCode


一、题目

给你一棵二叉树的根节点 root ,返回树的 最大宽度

树的 最大宽度 是所有层中最大的 宽度

每一层的 宽度 被定义为该层最左和最右的非空节点(即,两个端点)之间的长度。将这个二叉树视作与满二叉树结构相同,两端点间会出现一些延伸到这一层的 null 节点,这些 null 节点也计入长度。

题目数据保证答案将会在  32 位 带符号整数范围内。

二、示例

2.1> 示例 1:

图解LeetCode——662. 二叉树最大宽度(难度:中等)_算法

【输入】root = [1,3,2,5,3,null,9]
【输出】4
【解释】最大宽度出现在树的第 3 层,宽度为 4 (5,3,null,9) 。

2.2> 示例 2:

图解LeetCode——662. 二叉树最大宽度(难度:中等)_后端_02

【输入】root = [1,3,2,5,null,null,9,6,null,7]
【输出】7
【解释】最大宽度出现在树的第 4 层,宽度为 7 (6,null,null,null,null,null,7) 。

2.3> 示例 3:

图解LeetCode——662. 二叉树最大宽度(难度:中等)_广度优先_03

【输入】root = [1,3,2,5]
【输出】2
【解释】最大宽度出现在树的第 2 层,宽度为 2 (3,2) 。

提示:

  • 树中节点的数目范围是 [1, 3000]
  • -100 <= Node.val <= 100

三、解题思路

3.1> 思路1:广度优先 + 节点编号

根据题意,要统计树的最大宽度是所有层中最大的宽度。那么,既然涉及到要对每一层的树节点进行操作,我们会很自然的想到要用广度优先遍历。但是,如果采用广度优先算法,我们遇到的一个麻烦就是,如何去处理空节点?类似于,一个节点它只有一个子节点。这种空节点也是需要被统计在内的。所以,首先考虑的一个办法是,采用构建一个空的虚拟节点,由于题目中提示:-100 <= Node.val <= 100,所以我们可以指定虚拟节点的val值为-101,即:如果发现没有左子节点或者右子节点的话,我们就创建一个new TreeNode(-101, null, null)。这样,就可以构建一个全都有字节点的二叉树了。

那么,由于没有子节点就创建空的虚拟节点,如果不添加某个判断条件,这种构建空节点的操作将会无限的创建下去。那么我们可以通过判断某一层的节点值是否有非-101的,如果节点的val值都是-101,则说明这一层都是空节点,结束循环操作。如下图所示:

图解LeetCode——662. 二叉树最大宽度(难度:中等)_子节点_04

构建虚拟的空节点虽然可以满足题目的计算逻辑,但是,由于要大量的创建空的虚拟节点,而且越到层级越深且该层级真是节点越少,那么创建的空节点将会非常的多,那么在提交的时候,就会造成超出内存限制的问题。

图解LeetCode——662. 二叉树最大宽度(难度:中等)_深度优先_05

那么,除了创建虚拟节点,我们还有什么办法呢?其实,通过观察我们会发现一个规律:假设根节点的编号为1,左子节点为2,右子节点为3……以此类推,会得出如下结论:

root的编号=N
root.left的编号=2N
root.right的编号=2
N + 1

那么我们通过编号就可以计算同层中两个节点直间的距离了。我们还是以root = [1,3,2,5,null,null,9,6,null,7]为例,看看通过编号怎么去解题。

图解LeetCode——662. 二叉树最大宽度(难度:中等)_子节点_06

对于编号的存储,我们可以创建一个对象,里面包含编号和TreeNode这两个变量,也可以使用JDK内置的Pair对象,由于本题中,节点的val值没有任何用处,所以,编号我就存储到了val属性值中,这样更易于存储和获取。具体实现,请参照:4.1> 代码1:广度优先 + 节点编号

3.2> 思路2:深度优先 + 节点编号

既然广度优先可以解决该问题,那么理论上,通过深度优先也是可以解题的。由于深度优先,是先从最上层沿着一条父子关系链遍历的下层,类似一个分支一个分支的去遍历,那么,我们就需要一个Map来帮助我们存储层级与当前层级最小值的对应关系了——即:key=levelvalue=minValue。那么,我们每当遍历一个节点时,就可以通过当前的level值去获取最小节点值:

如果Map中不存在该level的最小值,则将该节点的值放入到map中作为当前level下的最小值;
如果存在,那么则用当前节点值node.val减去从Map中获取的当前level下的最小值;

我们还是以root = [1,3,2,5,null,null,9,6,null,7]为例,看看通过深度优先怎么去解题。请见下图:

图解LeetCode——662. 二叉树最大宽度(难度:中等)_算法_07

针对于采用深度优先算法的具体实现,请参照:4.2> 代码2:深度优先 + 节点编号

四、代码实现

4.1> 代码1:广度优先 + 节点编号

class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        int result = 0;
        Deque<TreeNode> deque = new ArrayDeque();
        deque.addLast(new TreeNode(1, root.left, root.right));
        while(!deque.isEmpty()) {
            int count = deque.size(), startIndex = -1, endIndex = -1;
            for (int i = 0; i < count; i++) {
                TreeNode node = deque.pop();
                endIndex = node.val;
                if (startIndex == -1) startIndex = node.val;
                if (node.left != null) deque.addLast(new TreeNode(node.val * 2, node.left.left, node.left.right));
                if (node.right != null) deque.addLast(new TreeNode(node.val * 2 + 1, node.right.left, node.right.right));
            }
            result = Math.max(result, endIndex - startIndex + 1);
        }
        return result;
    }
}

图解LeetCode——662. 二叉树最大宽度(难度:中等)_算法_08

4.2> 代码2:深度优先 + 节点编号

class Solution {
    int result = 0;
    Map<Integer, Integer> minValue = new HashMap();
    public int widthOfBinaryTree(TreeNode root) {
        depth(root, 1, 0);
        return result;
    }

    public void depth(TreeNode node , int nodeIndex, int level) {
        if (node == null) return;
        minValue.putIfAbsent(level, nodeIndex);
        result = Math.max(result, nodeIndex - minValue.get(level) + 1);
        depth(node.left, 2 * nodeIndex, level + 1);
        depth(node.right, 2 * nodeIndex + 1, level + 1);
    }
}

图解LeetCode——662. 二叉树最大宽度(难度:中等)_深度优先_09

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享

更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(^o^)/ ~ 「干货分享,每天更新」

标签:node,level,662,节点,二叉树,编号,null,root,LeetCode
From: https://blog.51cto.com/u_15003301/6330106

相关文章

  • 图解LeetCode——658. 找到 K 个最接近的元素(难度:中等)
    一、题目给定一个排序好的数组 arr,两个整数k和x,从数组中找到最靠近x(两数之差最小)的k个数。返回的结果必须要是按升序排好的。整数a比整数b更接近x需要满足:|a-x|<|b-x|或者|a-x|==|b-x|且a<b二、示例2.1>示例1:【输入】arr=[1,2,3,4,5],k=......
  • 图解LeetCode——剑指 Offer 36. 二叉搜索树与双向链表
    一、题目输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。二、示例为了让您更好地理解问题,以下面的二叉搜索树为例:我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对......
  • 图解LeetCode——782. 变为棋盘(难度:困难)
    一、题目一个 n*n 的二维网络 board 仅由 0 和 1 组成 。每次移动,你能任意交换两列或是两行的位置。返回将这个矩阵变为 “棋盘”  所需的最小移动次数 。如果不存在可行的变换,输出-1。“棋盘”是指任意一格的上下左右四个方向的值均与本身不同的矩阵。二、示例......
  • 图解LeetCode——1460. 通过翻转子数组使两个数组相等(难度:简单)
    一、题目给你两个长度相同的整数数组 target 和 arr 。每一步中,你可以选择 arr 的任意非空子数组 并将它翻转。你可以执行此过程任意次。如果你能让arr 变得与target 相同,返回True;否则,返回False。二、示例2.1>示例1:【输入】target=[1,2,3,4],arr=[2,4,1,3]......
  • 图解LeetCode——剑指 Offer 07. 重建二叉树
    一、题目输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。二、示例2.1>示例1:【输入】preorder=[3,9,20,15,7],inorder=[9,3,15,20,7]【输出】[3,9,20,null,null,15,7]2.2>示例2:【输入】pr......
  • 图解LeetCode——剑指 Offer 29. 顺时针打印矩阵
    一、题目输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。二、示例2.1>示例1:【输入】matrix=[[1,2,3],[4,5,6],[7,8,9]]【输出】[1,2,3,6,9,8,7,4,5]2.2>示例2:【输入】matrix= [[1,2,3,4],[5,6,7,8],[9,10,11,12]]【输出】[1,2,3,4,8,12,11,10,9,5,6,7]限......
  • 图解LeetCode——剑指 Offer 15. 二进制中1的个数
    一、题目编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为'1'的个数(也被称为 汉明重量)。二、示例2.1>示例1:【输入】n=11(控制台输入00000000000000000000000000001011)【输出】3【解释】输入的二进制串000000000000000000000000000010......
  • 图解LeetCode——654. 最大二叉树(难度:中等)
    一、题目给定一个不重复的整数数组 nums。 最大二叉树 可以用下面的算法从 nums递归地构建:1>创建一个根节点,其值为 nums中的最大值。2>递归地在最大值 左边 的 子数组前缀上 构建左子树。3>递归地在最大值右边的 子数组后缀上 构建右子树。返回 nums构建的......
  • 图解LeetCode——1224. 最大相等频率(难度:困难)
    一、题目给你一个正整数数组 nums,请你帮忙从该数组中找出能满足下面要求的最长前缀,并返回该前缀的长度:从前缀中恰好删除一个元素后,剩下每个数字的出现次数都相同。如果删除这个元素后没有剩余元素存在,仍可认为每个数字都具有相同的出现次数(也就是0次)。二、示例2.1>示例1:【......
  • 图解LeetCode——769. 最多能完成排序的块(难度:中等)
    一、题目给定一个长度为n的整数数组arr,它表示在[0,n-1]范围内的整数的排列。我们将arr分割成若干块(即分区),并对每个块单独排序。将它们连接起来后,使得连接的结果和按升序排序后的原数组相同。返回数组能分成的最多块数量。二、示例2.1>示例1:【输入】arr=[4,3,2,......