二叉树的层序遍历
链接
文章链接
题目链接
102. 二叉树的层序遍历
107. 二叉树的层序遍历 II
199. 二叉树的右视图
637. 二叉树的层平均值
429. N 叉树的层序遍历
515. 在每个树行中找最大值
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针II
104. 二叉树的最大深度
111.二叉树的最小深度
看完文章后的想法
这篇文章总共10道题,都是利用层序遍历解决,层序遍历类似于图中的广度优先搜索(BFS),由于之前了解过层序遍历,所以直接上这10道题的代码。
102. 二叉树的层序遍历
function levelOrder(root: TreeNode | null): number[][] {
let queue:TreeNode[]=[]
let res:number[][]=[]
if(root) queue.push(root)
while(queue.length>0){
let size:number=queue.length
let arr:number[]=[]
while(size--){//这里必须提前存储每层的节点个数
let node:TreeNode=queue.shift()!
arr.push(node.val)
if(node.left) queue.push(node.left)
if(node.right) queue.push(node.right)
}
res.push(arr)
}
return res
};
107. 二叉树的层序遍历 II
107这道题只需要把102这道题最后的res结果反一下就可以了,代码如下:
function levelOrderBottom(root: TreeNode | null): number[][] {
let queue:TreeNode[]=[]
let res:number[][]=[]
if(root) queue.push(root)
while(queue.length>0){
let size:number=queue.length
let arr:number[]=[]
while(size--){
let node:TreeNode=queue.shift()!
arr.push(node.val)
if(node.left) queue.push(node.left)
if(node.right) queue.push(node.right)
}
res.push(arr)
}
return res.reverse()
};
199. 二叉树的右视图
这道题只需要取每层数组的最后数就可以了
function rightSideView(root: TreeNode | null): number[] {
let queue:TreeNode[]=[]
let res:number[]=[]
if(root) queue.push(root)
while(queue.length>0){
let size:number=queue.length
let arr:number[]=[]
while(size--){
let node:TreeNode=queue.shift()!
arr.push(node.val)
if(node.left) queue.push(node.left)
if(node.right) queue.push(node.right)
}
res.push(arr[arr.length-1])
}
return res
};
637. 二叉树的层平均值
这道题只需要把每层的arr直接求和再平均就可以了
function averageOfLevels(root: TreeNode | null): number[] {
let queue:TreeNode[]=[]
let res:number[]=[]
if(root) queue.push(root)
while(queue.length>0){
let size:number=queue.length
let aver:number=size
let sum:number=0
while(size--){
let node:TreeNode=queue.shift()!
sum+=node.val
if(node.left) queue.push(node.left)
if(node.right) queue.push(node.right)
}
res.push(sum/aver)
}
return res
};
429. N 叉树的层序遍历
只需要把left和right换成children就可以了
function levelOrder(root: Node | null): number[][] {
let queue:Node[]=[]
let res:number[][]=[]
if(root) queue.push(root)
while(queue.length>0){
let size:number=queue.length
let arr:number[]=[]
while(size--){
let node:Node=queue.shift()!
arr.push(node.val)
queue.push(...node.children)
}
res.push(arr)
}
return res
};
515. 在每个树行中找最大值
function largestValues(root: TreeNode | null): number[] {
let queue:TreeNode[]=[]
let res:number[]=[]
if(root) queue.push(root)
while(queue.length>0){
let size:number=queue.length
let num:number=Number.MIN_SAFE_INTEGER
while(size--){
let node:TreeNode=queue.shift()!
num=Math.max(num,node.val) //找每一层的最大值
if(node.left) queue.push(node.left)
if(node.right) queue.push(node.right)
}
res.push(num)
}
return res
};
116.填充每个节点的下一个右侧节点指针
这道题的null条件是size==0 因为size等于0说明是这一层的最后一个
function connect(root: Node | null): Node | null {
let queue:Node[]=[]
if(root) queue.push(root)
while(queue.length>0){
let size:number=queue.length
while(size--){
let node:Node=queue.shift()!;
node.next=(size==0?null:queue[0]);
if(node.left) queue.push(node.left);
if(node.right) queue.push(node.right);
}
}
return root
};
117.填充每个节点的下一个右侧节点指针II
和116题的代码完全一致
function connect(root: Node | null): Node | null {
let queue:Node[]=[]
if(root) queue.push(root)
while(queue.length>0){
let size:number=queue.length
while(size--){
let node:Node=queue.shift()!;
node.next=(size==0?null:queue[0]);
if(node.left) queue.push(node.left);
if(node.right) queue.push(node.right);
}
}
return root
};
104. 二叉树的最大深度
这道题只需要看while(queue.length>0)循环几次就可以了
function maxDepth(root: TreeNode | null): number {
let queue:TreeNode[]=[]
let res:number=0
if(root) queue.push(root)
while(queue.length>0){
let size:number=queue.length
while(size--){
let node:TreeNode=queue.shift()!
if(node.left) queue.push(node.left)
if(node.right) queue.push(node.right)
}
res++
}
return res
};
111.二叉树的最小深度
function minDepth(root: TreeNode | null): number {
let queue:TreeNode[]=[]
let res:number=0
if(root) queue.push(root)
while(queue.length>0){
let size:number=queue.length
while(size--){
let node:TreeNode=queue.shift()!
if(node.left) queue.push(node.left)
if(node.right) queue.push(node.right)
if(!node.left&&!node.right) return res+1 //如果这个节点没有left和right说明是叶子节点 则是最小的深度 加一是因为res从0开始算的
}
res++
}
return 0
};
思考
层序遍历是有模板的,上面看似是十道题,其实都是通过一个模板修改得来的,不算太难,主要是熟练二叉树层序遍历的模板
226. 翻转二叉树
链接
文章链接
题目链接
第一想法
二叉树解决问题一般有三种方法,层序、递归、迭代,这道题感觉不太能用层序的方法,所以尝试用递归和迭代的方法
递归法
//前序遍历
function invertTree(root: TreeNode | null): TreeNode | null {
const foo:(root: TreeNode | null)=>void=(root: TreeNode | null)=>{
if(!root) return
[root.left,root.right]=[root.right,root.left];//中
if(root.left) foo(root.left) //左
if(root.right) foo(root.right) //右
}
foo(root)
return root
};
迭代法
//前序遍历
function invertTree(root: TreeNode | null): TreeNode | null {
let queue:TreeNode[]=[]
if(root) queue.push(root)
while(queue.length>0){
let node:TreeNode=queue.shift()!;
[node.left,node.right]=[node.right,node.left]; //中
if(node.left) queue.push(node.left)//左
if(node.right) queue.push(node.right)//右
}
return root
};
看完文章后的想法
由于这道题的核心思想是把节点的左右孩子翻转,所以递归、迭代以及层序遍历都是可以的,但是中间最要让人注意的是中序遍历,代码如下:
function invertTree(root: TreeNode | null): TreeNode | null {
const foo:(root: TreeNode | null)=>void=(root: TreeNode | null)=>{
if(!root) return
if(root.left) foo(root.left);
[root.left,root.right]=[root.right,root.left];//中
if(root.left) foo(root.left) //这里还是left 因为上面把左右节点颠倒了
}
foo(root)
return root
};
思考
这道题应该是一道非常基础的题,但是其中蕴含的思想却是很多的,写二叉树的题首先要想好用什么方法以及如何遍历,这道题就是前中后序遍历都可以,但是中序遍历有个坑,只要把坑了解之后,就没什么问题了。
101. 对称二叉树
链接
文章链接
题目链接
第一想法
这道题我的第一想法是层序遍历,但是没写出来,然后看到进阶是用迭代法和递归法,想了想知道是如果是对称的二叉树,那么翻转二叉树还是和原来一样的,但是感觉不是那么好写,难道再造一个翻转后的比较? 最终决定先看文章再写。
看完文章后的想法
看完文章后我发现我的思路有点锁死了,其实直接比较就可以了,核心思想是左节点的左孩子要和右节点的右孩子进行比较,同时左节点的右孩子要和右节点的左孩子进行比较。比较结束后要把结果值返回给上一层的节点,这样层层向上才能得到最终结果,所以这道题的核心思想是要用后序遍历,代码如下
function isSymmetric(root: TreeNode | null): boolean {
if(!root) return true
const compare:(left: TreeNode | null,right: TreeNode | null)=>boolean=(left: TreeNode | null,right: TreeNode | null)=>{
if(left&&!right) return false //左节点存在右节点不存在
if(!left&&right) return false //左节点不存在而右节点存在
if(!left&&!right) return true //左右节点都不在
if(left!.val!=right!.val) return false //左右节点都存在 但val值不相等
return compare(left!.left,right!.right)&&compare(left!.right,right!.left) //左右节点存在且值相等,则继续比较他们的孩子节点
}
return compare(root.left,root.right)
};
下面是迭代法的写法:
function isSymmetric(root: TreeNode | null): boolean {
let queue:(TreeNode|null)[]=[] //用栈也是可以的
if(root){
queue.push(root.left)
queue.push(root.right)
}
while(queue.length>0){
let left:TreeNode|null=queue.shift() as TreeNode|null
let right:TreeNode|null=queue.shift() as TreeNode|null
if(!left&&!right) continue;//左右节点都不存在则进下一层循环
if(!left||!right||(left.val!=right.val)) return false //左右节点其中一个不存在或值不相等直接说明不是对称二叉树
queue.push(left.left)
queue.push(right.right)
queue.push(left.right)
queue.push(right.left)
}
return true
};
思考
这道题我认为比较难想,可能是思路比较局限,这道题核心就在于是后序遍历,因为要拿到孩子节点的返回值才能确定是否是对称的二叉树,同时这也是我第一次没有做出来的题。
总结
今天共计12道题,其中前10道都是练习二叉树的层序遍历,比较简单,后面那两道题就比上面层序遍历的题难了,最难的应该是对称二叉树,没有想到,今日用时3.5小时