110. 平衡二叉树
链接
题目链接:(110. 平衡二叉树 - 力扣(Leetcode))
文章链接:(代码随想录 (programmercarl.com))
第一想法
我觉得利用层序法和递归法都可以解决这个问题,层序法判断每层的个数是不是,(n=0、1、2....),如果不是则判断queue是否为空,为空就是平衡二叉树,不为空就不是平衡二叉树,代码如下:
//层序法 没有完全通过测试 219/228
function isBalanced(root: TreeNode | null): boolean {
let queue:(TreeNode|null)[]=[]
let count:number=0
let boolean:boolean=false
if(root) queue.push(root)
while(queue.length>0){
let size:number=queue.length
if(size!=2**count) boolean=true
while(size--){
let node:TreeNode=queue.shift()!
if(node.left) queue.push(node.left)
if(node.right) queue.push(node.right)
}
count++
if(boolean==true) return queue.length==0
}
return true
};
写完后,以为可以通过测试,结果发现有个没有通过,就是root=[1,2,3,4,5,6,null,8]
看到这个例子我就知道我对平衡二叉树的理解有误,它是左右子树的高度相差不超过一,所以我那种解法错过了这种平衡二叉树。
由于写层序遍历时的错误,所以我觉得应该用后序遍历来解决这个问题,当前节点需要拿到左右节点的高度来计算是否为平衡二叉树,代码如下:
//递归法 可以通过用例
function isBalanced(root: TreeNode | null): boolean {
let boo: boolean = true //用于判断是否是平衡二叉树
const dfs: (root: TreeNode | null) => number = (root: TreeNode | null) => {
if (root == null) return 0
let left: number = dfs(root.left)
let right: number = dfs(root.right)
boo = boo && Math.abs(left - right) <= 1 //进行判断 用&&防止boo为false之后被覆盖为true
return 1 + Math.max(left, right) //反正节点的高度
}
dfs(root)
return boo
};
看完文章后的想法
代码随想录中用了迭代法和递归法,递归法和我想的是一样的,都是用后序遍历,但是判断是不是平衡二叉树是不一样的,代码随想录中是返回-1作为终止条件,代码如下:
function isBalanced(root: TreeNode | null): boolean {
c'''''''''-'';/-'//+/
\onst dfs:(root: TreeNode | null)=>number=(root: TreeNode | null)=>{
if(root==null) return 0
let left:number=dfs(root.left)
if(left==-1) return -1
let right:number=dfs(root.right)
if(right==-1) return -1
if(Math.abs(left-right)>1) return -1
return 1+Math.max(left,right)
}
return dfs(root)!=-1
};
文章中也使用了迭代法的解法,但是效率很低,其核心思路为先求得每个节点的深度,之后判断左右子树的深度是否差值大于1,先用统一模版的迭代法求每个节点的深度,之后再去求高度差(深度差)是否大于1,详细代码在代码随想录中。
思考
这道题我原本想用层序法和递归法,但是我用层序法是理解错了平衡二叉树的概念,平衡二叉树:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。所以后面转为用递归法解决这个问题,通过后序遍历可以完美解决这个问题。对于迭代法,我认为我还得多加理解。
257. 二叉树的所有路径
链接
文章链接
题目链接
第一想法
这道题首先能想到用递归解决,终止条件为当前节点的左右孩子都为null,除了第一个节点外,每增加一个节点,都需要增加一个'->',代码如下:
function binaryTreePaths(root: TreeNode | null): string[] {
let res:string[]=[]
const dfs:(root: TreeNode | null,str:string)=>void=(root: TreeNode | null,str:string)=>{
if(!root!.left&&!root!.right){
if(str) str+='->'+root!.val //用于防止root是一个节点的情况
else str=str+root!.val
res.push(str)
return
}
if(str) str+='->'+root!.val
else str=str+root!.val //开头节点
if(root!.left) dfs(root!.left,str)
if(root!.right) dfs(root!.right,str)//这里的str其实用到了回溯
}
dfs(root,"")
return res
};
看完文章后的想法
文章也是用的递归法和迭代法,迭代法的内容不太好理解,但是能用递归写的迭代法大部分可以写出来,而文章中用的迭代法,很明显的运用到了回溯,而我也运用到了回溯,但是不太明显,代码随想录中用一张图很明显的表现了回溯的过程:
不过我和代码随想录中的解法也有所不同,代码随想录中用数组存放路径,最后再合成路径,详细代码可查看代码随想录中。
思考
这道题不难,大部分人都可以想到迭代法,但是其中蕴含的回溯的想法可能大家都没注意,比较难的是迭代法的解法。迭代法中控制节点的数组和路径的数组要一致才能正确的解除答案。
404. 左叶子之和
链接
题目链接
文章链接
第一想法
还是用递归法,终止条件还是叶子节点,不过不一样的是有一个辅助条件,就是左节点只能从node.left中取得,不能从node.right中取得,所以递归中遇到node.right就设置为false,答案不加它,遇到node.left设置为true则加上它,详细代码如下:
function sumOfLeftLeaves(root: TreeNode | null): number {
let count:number=0
const dfs:(root: TreeNode | null,boo:boolean)=>void=(root: TreeNode | null,boo:boolean)=>{
if(!root!.left&&!root!.right){
boo&&(count+=root!.val)
return
}
if(root!.left)dfs(root!.left,true)
if(root!.right)dfs(root!.right,false)
}
dfs(root,false)
return count
};
看完文章后的想法
文章也用了递归法和迭代法,这里主要详细说说递归法,我的解法和文章中的解法不太一样,主要区别为我构造了一个辅助变量帮助我解决问题,而代码随想录中并没有,它是通过直接判断左节点来完成的,左节点是:如果节点A有左孩子B,而B既没有左孩子又没有右孩子,则B就是左节点,代码如下:
function sumOfLeftLeaves(root: TreeNode | null): number {
let count:number=0
const dfs:(root: TreeNode | null)=>void=(root: TreeNode | null)=>{
if(root==null) return
if(root.left==null&&root.right==null) return
if(root.left&&!root.left.left&&!root.left.right){
count+=root.left.val
}
dfs(root.left)
dfs(root.right)
}
dfs(root)
return count
};
思考
这道题不难,有多种解法,其中我的递归解法和代码随想录中的解法不一样,代码随想录中的解法优于我自己的解法,因为我用到了辅助变量,所以有一定的缺点。
总结
今日三道题都不难,但是或多或少都踩了坑,也看到了更优秀的解法,今日耗时2.5小时