代码随想录制day17
目录
110.平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
返回 true 。
题外话:
咋眼一看这道题目和 104二叉树的最大深度很像,其实有很大区别。
这里强调一波概念:
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。
但leetcode中强调的深度和高度很明显是按照节点来计算的,如图:
关于根节点的深度究竟是1 还是 0,不同的地方有不一样的标准,leetcode的题目中都是以节点为一度,即根节点深度是1。但维基百科上定义用边为一度,即根节点的深度是0,我们暂时以leetcode为准(毕竟要在这上面刷题)。
有的同学一定疑惑,为什么104.二叉树的最大深度中求的是二叉树的最大深度,也用的是后序遍历。
那是因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这棵树的最大深度,所以才可以使用后序遍历。
本题思路:
递归:
此时大家应该明白了既然要求比较高度,必然是要后序遍历。
1、明确递归函数的参数和返回值
参数:当前传入节点。
返回值:以当前传入节点为根节点的树的高度。
那么如何标记左右子树是否差值大于1呢?如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。
2、明确终止条件
递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0
3、明确单层递归的逻辑
如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。
分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则则返回-1,表示已经不是二叉平衡树了。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func isBalanced(root *TreeNode) bool {
if root == nil {
return true
}
if isBalanced(root.Left) == false || isBalanced(root.Right) == false {
return false
}
l_high := maxdepth(root.Left)
r_high := maxdepth(root.Right)
if abs(l_high - r_high) > 1 {
return false
}
return true
}
func maxdepth(root *TreeNode) int {
if root == nil {
return 0
}
return max(maxdepth(root.Left), maxdepth(root.Right)) + 1
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func abs(a int) int {
if a < 0 {
return -a
}
return a
}
257. 二叉树的所有路径
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
思路:
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一一个路径在进入另一个路径。
前序遍历以及回溯的过程如图:
我们先使用递归的方式,来做前序遍历。要知道递归和回溯就是一家的,本题也需要回溯。
var res []string
func binaryTreePaths(root *TreeNode) []string {
res = make([]string,0)
digui(root,"")
return res
}
func digui(root *TreeNode, s string) {
if root.Left == nil && root.Right == nil {
v := s + strconv.Itoa(root.Val)
res = append(res, v)
return
}
s = s + strconv.Itoa(root.Val) + "->" // 中
if root.Left != nil { // 左
digui(root.Left, s)
}
if root.Right != nil { // 右
digui(root.Right, s)
}
}
//以下面例子为例,代码执行过程如下
1
2 3
1、s = "" + "1" + "->" // "1->"
2、root.Left != nil,进行递归
此时在root.Left里面的root.Left和root.Right == nil {
v := s + "root.Val" // "1->" + "2" = "1->2"
res = append(res,v ) //存放进res, res = ["1->2"]
return // 回溯了,结束该递归函数
}
3、root.Right != nil,进行递归
此时root.Right里面的root.Left和root.Right == nil {
v := s + "root.Val" // "1->" + "3" = "1->3"
res = append(res, v) //存放进res, res = ["1->2", "1->3"]
return
}
404. 左叶子之和
计算给定二叉树的所有左叶子之和。
示例:
思路:
首先要注意是判断左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历。
因为题目中其实没有说清楚左叶子究竟是什么节点,那么我来给出左叶子的明确定义:如果左节点不为空,且左节点没有左右孩子,那么这个节点的左节点就是左叶子
大家思考一下如下图中二叉树,左叶子之和究竟是多少?
其实是0,因为这棵树根本没有左叶子!
但看这个图的左叶子之和是多少?
相信通过这两个图,大家可以最左叶子的定义有明确理解了。
那么判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。
如果该节点的左节点不为空,该节点的左节点的左节点为空,该节点的左节点的右节点为空,则找到了一个左叶子,判断代码如下
if node.Left != nil && node.Left.Left == nil && node.Left.Right == nil {
找到了一个左叶子节点;
左叶子节点处理逻辑;
}
递归法:
递归的遍历顺序为后序遍历(左右中),是因为要通过递归函数的返回值来累加求取左叶子数值之和。
递归三部曲:
1、确定递归函数的参数和返回值
判断一个树的左叶子节点之和,那么一定要传入树的根节点,递归函数的返回值为数值之和,所以int使用题目中给出的函数就可以了。
2、确定终止条件
if root == nil{
return 0
}
3、确定单层递归的逻辑
当遇到左叶子节点的时候,记录数值,然后通过递归求取左子树左叶子之和,和 右子树左叶子之和,相加便是整个树的左叶子之和。
func sumOfLeftLeaves(root *TreeNode) int {
var res int
findLeft(root,&res)
return res
}
func findLeft(root *TreeNode,res *int){
//重要代码
if root.Left!=nil&&root.Left.Left==nil&&root.Left.Right==nil{
*res=*res+root.Left.Val
}
if root.Left!=nil{
findLeft(root.Left,res)
}
if root.Right!=nil{
findLeft(root.Right,res)
}
}
今天脑壳有点懵。再来。