It isn't the big pleasures that count the most; it's making a great deal out of the little ones.

最重要的不是有大快乐,而是能充分享受小快乐。

问题描述

来源:LeetCode第662题

难度:中等


给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。


每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。


示例 1:



输入:            1         /   \        3     2       / \     \        5   3     9 
输出: 4解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。

示例 2:



输入:           1         /          3           / \             5   3     
输出: 2解释: 最大值出现在树的第 3 层,宽度为 2 (5,3)。

示例 3:



输入:           1         / \        3   2        /              5      
输出: 2解释: 最大值出现在树的第 2 层,宽度为 2 (3,2)。

示例 4:



输入:           1         / \        3   2       /     \        5       9      /         \    6           7输出: 8解释: 最大值出现在树的第 4 层,宽度为 8 (6,null,null,null,null,null,null,7)。


注意: 答案在32位有符号整数的表示范围内。


BFS解决

二叉树的最大宽度我们可以认为是从最左边到最右边的最大距离,假如是一棵满二叉树的话,每一层的最大距离就是最左边到最右边的节点数。因为二叉树不一定都是满二叉树,有可能像下面这样。

564,二叉树最大宽度_Java练习题二叉树

每一行从最左边到最右边我们很容易想到的就是二叉树的BFS遍历,他就是一层一层遍历的,关于二叉树的BFS不明白的可以看下下面的视频。

所以题思路很容易想到,就是遍历每一层的时候计算这一层最左边节点到最右边节点的距离,大致代码如下

 1public int widthOfBinaryTree(TreeNode root) {
2 if (root == null)
3 return 0;
4 //使用双端队列
5 Deque<TreeNode> queue = new LinkedList<>();
6 //把根节点加入到队列中
7 queue.offer(root);
8 //记录最大的宽度
9 int maxWide = 0;
10 while (!queue.isEmpty()) {
11 //当前层节点的数量
12 int levelCount = queue.size();
13 // int gap = 当前层最左边到最右边的距离
14 maxWide = Math.max(maxWide, gap);
15 //遍历当前层的所有节点,把他们的子节点在加入到队列中
16 for (int i = 0; i < levelCount; i++) {
17 TreeNode node = queue.poll();
18 //如果左子节点不为空,就把左子节点加入到队列中
19 if (node.left != null) {
20 queue.offer(node.left);
21 }
22 //如果右子节点不为空,就把右子节点加入到队列中
23 if (node.right != null) {
24 queue.offer(node.right);
25 }
26 }
27 }
28 return maxWide;
29}

他就是从上往下一层一层遍历的,遍历每一层的时候我们需要计算当前层的最大距离,保留最大的即可。这里关键是怎么计算当前层的最大距离。


我们可以这样来计算,把它想象成为一颗
满二叉树,假如根节点是遍历的第1个节点,那么他的两个子节点分别是遍历的第2个和第3个节点。并且可以推算出如果一个节点是第n个遍历的,那么他的两个子节点分别是第n*2和n*2+1个遍历的,具体我们来画个图看一下

564,二叉树最大宽度_Java练习题二叉树_02

我们可以把这些值存到一个map中,也可以把它直接存到节点中,这里我们就把他存到节点中,在遍历每一层的时候用当前层最右边的值减去最左边的值+1就是当前层的最大距离,来看下最终代码。


 1public int widthOfBinaryTree(TreeNode root) {
2 if (root == null)
3 return 0;
4 //使用双端队列
5 Deque<TreeNode> queue = new LinkedList<>();
6 //把根节点加入到队列中
7 queue.offer(root);
8 //根节点的值我们把它修改为1
9 root.val = 1;
10 //记录最大的宽度
11 int maxWide = 0;
12 while (!queue.isEmpty()) {
13 //当前层节点的数量
14 int levelCount = queue.size();
15 //当前层最左边节点的值
16 int left = queue.peekFirst().val;
17 //当前层最右边节点的值
18 int right = queue.peekLast().val;
19 //当前层的最大宽度就是right - left + 1,
20 //这里计算之后要保留最大的
21 maxWide = Math.max(maxWide, right - left + 1);
22 //遍历当前层的所有节点,把他们的子节点在加入到队列中
23 for (int i = 0; i < levelCount; i++) {
24 TreeNode node = queue.poll();
25 int position = node.val;
26 //如果左子节点不为空,就把左子节点加入到队列中
27 if (node.left != null) {
28 node.left.val = position * 2;
29 queue.offer(node.left);
30 }
31 //如果右子节点不为空,就把右子节点加入到队列中
32 if (node.right != null) {
33 node.right.val = position * 2 + 1;
34 queue.offer(node.right);
35 }
36 }
37 }
38 return maxWide;
39}

这里因为左边节点先入队,所以peekFirst()返回的就是当前层最左边的节点,右边节点是最后入队的,所以peekLast()返回的是当前层最右边的节点。


DFS解决

BFS是一层一层的打印,DFS是沿着一个分支一直走下去,当到达叶子节点的时候在往回走。所以这题解题思路也很简单,我们使用两个变量left和right


  • left变量记录每一层最左边的节点。



  • right变量记录每一层遍历过的最右边节点。




然后再每一层计算最右边到最左边的距离,也就是当前层的最大宽度,来看下代码

 1//记录最大的宽度
2int maxWide = 0;
3
4public int widthOfBinaryTree(TreeNode root) {
5 dfs(root, 0, 1, new ArrayList<>(), new ArrayList<>());
6 return maxWide;
7}
8
9/**
10 * @param root
11 * @param level 遍历到第几层
12 * @param position 每个节点在满二叉树中的位置
13 * @param left 只记录最左边的节点,每层只记录一个
14 * @param right 只记录遍历过的最右边节点,每层只记录一个(这里是遍历过的,如果当前层有更右边的节点,
15 * 会把当前层的替换)
16 */
17public void dfs(TreeNode root, int level, int position, List<Integer> left, List<Integer> right) {
18 if (root == null)
19 return;
20 //首次到当前层,要把当前值分别加入到left和right中
21 if (left.size() == level) {
22 left.add(position);
23 right.add(position);
24 } else {//如果当前层已经遍历过,会替换掉
25 right.set(level, position);
26 }
27 //递归遍历下一层的左右子节点
28 dfs(root.left, level + 1, position << 1, left, right);
29 dfs(root.right, level + 1, position * 2 + 1, left, right);
30 //计算当前层的最大间距,保留最大值
31 maxWide = Math.max(maxWide, right.get(level) - left.get(level) + 1);
32}




截止到目前我已经写了500多道算法题了,为了方便大家阅读,我把部分算法题整理成了pdf文档,目前有1000多页,大家可以在公众号中回复关键字“pdf”即可获取下载链接。


564,二叉树最大宽度_Java练习题二叉树_03你点的每个赞,我都认真当成了喜欢