662. 二叉树最大宽度【中】
思路1:广度优先+完全二叉树序号
直接思路肯定是从层序遍历开始,首先想到是除了层序遍历需要的队列,额外维护一个队列,用于存放层序遍历时node在完全二叉树对应的编号,这个队列存放编号和层序遍历的队列存放的节点是一一对应的,且操作保持一致(一起pop,一起push)。然后每一层遍历时,就将队列的最后一个编号和第1个编号相减+1就是这一层的宽度,如上,最大宽度是4,因为7-4+1。最后保留一个最大宽度即可。
代码:
//t:O(N) s:O(N) 和广度优先一致
class Solution {
public:
int widthOfBinaryTree(TreeNode* root) {
if(!root) return 0;
// 层序遍历所需队列
queue<TreeNode *> q;
// 完全二叉树编号队列
queue<int> number;
q.push(root);
number.push(1);
int maxwidth =0;
int right=0,left=0;
// 层序遍历
while(q.size()!=0){
int len = q.size();
for(int i =0 ;i<len;i++){
TreeNode* node = q.front();
int parentnum = number.front();
if(node->left){
q.push(node->left);
number.push(parentnum*2); // 左孩子编号
}
if(node->right){
q.push(node->right);
number.push(parentnum*2+1); // 右孩子编号
}
if(i==0) left = parentnum;
if(i==len-1) right = parentnum;
number.pop();
q.pop();
}
if((right-left+1)>maxwidth) maxwidth = right-left+1;
}
return maxwidth;
}
};
然而试例给了一棵非常高的树,使得完全二叉树编号超出了32位int(题目告知)的范围2147483647。
看了一下官方的答案,用了unsigned long long(64位)解决问题,还使用了很多技巧,最后简化一下代码:
class Solution {
public:
int widthOfBinaryTree(TreeNode* root) {
//pair是将2个数据组合成一组数据的一种数据类型
//使用vector而非queue完成
vector<pair<TreeNode*,unsigned long long>> node_arr;
//emplace_back比push_back效率更高(实际上提交并没有展现出来)
node_arr.emplace_back(root,1L);
unsigned long long width=1,max=1;
// 开始广度优先
while(!node_arr.empty()){
// 每次都生成一个tmp,用于存放下一层的数据
vector<pair<TreeNode*,unsigned long long>> tmp;
for(auto &[node,index]:node_arr){
if(node->left) tmp.emplace_back(node->left,index*2);
if(node->right) tmp.emplace_back(node->right,index*2+1);
}
//计算当前层数据,这里很重要,很容易写成tmp.back().second - tmp[0].second+1
//而当遍历到最后一层的时候,再计算下一层的宽会是空的,所以遍历一层,计算一层宽
width = node_arr.back().second - node_arr[0].second+1;
if(width>max) max = width;
// 直观理解:掏空tmp,将tmp的值交给node_arr进行数据拷贝
node_arr = move(tmp);
}
return max;
}
};
思路2 深度优先
瞥见了有深度优先的方法,研究了一下,感觉也很有意思。
同样利用了完全二叉树的编号,使用了三个很重要的变量,depth二叉树深度,id即二叉树编号,以及哈希表level。level记录所有层最左边结点的编号。每次递归,如果level中没有key为depth的值,说明到了新的一层,且访问的是该层最左端(深度优先遍历的性质),记录id值;如果有,那么计算当前节点id-level[depth]+1就是当前节点距离当前层最左边结点的宽度,不断递归,返回最大宽度。
// s:O(n)递归深度 t:O(n)遍历所有点
using ULL = unsigned long long;
class Solution {
public:
unordered_map<int,int> level; //没有记录节点,不算入空间复杂度
int widthOfBinaryTree(TreeNode* root) {
return dfs(root,0,1ULL);
}
int dfs(TreeNode* node, int depth,ULL id){
if(!node) return 0;
if(!level.count(depth)) level[depth] = id; //如果没访问过,就是最左边的id
int width = id-level[depth]+1; //用当前节点id减去最左边节点id
int left = dfs(node->left,depth+1,id*2); // 去左孩子看看
int right = dfs(node->right,depth+1,id*2+1); // 去右孩子看看
return max({width,left,right}); // max的技巧,当比较三个的时候加个{}
}
};