大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。
题目
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位有符号整数的表示范围内。
思路
- 这道题目的意思给大家翻译一下就是,每一层的所有节点,前后都可以去除Null的部分,直到有值为止,然后判断有效部分的最大长度;
- 那么照这个思路,我第一个想到的是用一个数组把每一轮的值都存起来,然后再用一个方法去去除左右的空位,这样子每一轮得到的值进行长度的判断,取上一轮的最大长度和当前长度的最大值。
暴力解法
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var widthOfBinaryTree = function(root) {
let maxCount = 0;
// 记录上一轮的列表数据
let prevArr = [ root ];
while (filterRightNullData(prevArr).length) {
maxCount = Math.max(maxCount, filterRightNullData(prevArr).length);
prevArr = prevArr.reduce((total, cur) => {
total.push(cur && cur.left);
total.push(cur && cur.right);
return total;
}, []);
}
return maxCount;
};
// 删除两边的Null元素
function filterRightNullData(arr) {
while (arr.length && arr[0] === null) {
arr.shift();
}
while (arr.length && arr[arr.length - 1] === null) {
arr.pop();
}
return arr;
}
翻车现场
事实告诉我,暴力解法不可取,内存空间溢出了,所以我们需要找规律。我又想到我们没必要每一轮把Null节点也统计进去,我们大可以只统计有效的节点,同时我们需要给有效的节点添加一个坐标值,记录一下它是第几个,这样子把左右有效节点的坐标值相减一样可以达到我们要的效果。
再次翻车代码
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var widthOfBinaryTree = function(root) {
if (!root) return 0;
// 记录上一轮的列表数据
let prevArr = [ { node: root, index: 0 } ];
// 记录最大长度
let maxCount = 1;
while (prevArr.length) {
// 超过两个节点才可以判断索引差, 记得差和数量之间需要 + 1
if (prevArr.length > 1) {
let curCount = prevArr[prevArr.length - 1].index - prevArr[0].index + 1;
maxCount = Math.max(maxCount, curCount);
}
prevArr = prevArr.reduce((total, cur) => {
// 非空节点才进行存储
// 索引关系, 左节点等于2n,右节点等于2n + 1
cur.node.left && total.push({ node: cur.node.left, index: cur.index * 2 });
cur.node.right && total.push({ node: cur.node.right, index: cur.index * 2 + 1 });
return total;
}, []);
}
return maxCount;
};
事故现场
原因
由于题目中说了,答案在32位有符号整数的表示范围内,这意味着结果可能特别大,我们用普通的number可能无法去存储这么大的数字, 所以我们要对大数进行一个取余操作。
最终代码
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var widthOfBinaryTree = function(root) {
if (!root) return 0;
// 记录上一轮的列表数据
let prevArr = [ { node: root, index: 0 } ];
// 记录最大长度
let maxCount = 1;
// 数字可能过大,会溢出,所以我们要进行取余操作
let mod = Math.pow(2, 32);
while (prevArr.length) {
// 超过两个节点才可以判断索引差, 记得差和数量之间需要 + 1
if (prevArr.length > 1) {
let curCount = prevArr[prevArr.length - 1].index - prevArr[0].index + 1;
maxCount = Math.max(maxCount, curCount);
}
prevArr = prevArr.reduce((total, cur) => {
// 非空节点才进行存储
// 索引关系, 左节点等于2n,右节点等于2n + 1, 数字可能过大需要取余
const curIndex = cur.index * 2 % mod;
cur.node.left && total.push({ node: cur.node.left, index: curIndex });
cur.node.right && total.push({ node: cur.node.right, index: curIndex + 1 });
return total;
}, []);
}
return maxCount;
};
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。