110. 平衡二叉树
文章目录
- 110. 平衡二叉树
- 题外话
- 本题思路
- 递归
110. 平衡二叉树
110. 平衡二叉树
给定一个二叉树,判断它是否是 平衡二叉树
平衡二叉树
是指该树所有节点的左右子树的深度相差不超过 1
。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:true
示例 2:
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
示例 3:
输入:root = []
输出:true
提示:
- 树中的节点数在范围 [0, 5000] 内
- -10^4 <= Node.val <= 10^4
题外话
咋眼一看这道题目和104. 二叉树的最大深度(包括N叉树的最大深度)很像,其实有很大区别。
这里强调一波概念:
二叉树节点的深度
:指从根节点
到该节点
的最长简单路径边的条数。二叉树节点的高度
:指从该节点
到叶子节点
的最长简单路径边的条数。
但leetcode
中强调的深度和高度很明显是按照节点来计算的,如图:
关于根节点的深度究竟是1
还是 0
,不同的地方有不一样的标准,leetcode
的题目中都是以节点为一度,即根节点深度是1
。但维基百科上定义用边为一度,即根节点的深度是0
,我们暂时以leetcode
为准(毕竟要在这上面刷题)。
因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)
,可以理解为深度是从上往下
不断增加,而高度是从下往上
不断增加。
有的同学一定疑惑,为什么104.二叉树的最大深度
中求的是二叉树的最大深度,也用的是后序遍历。
那是因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这棵树的最大深度,所以才可以使用后序遍历。如果是求某个中间节点的深度,则使用后序遍历可能会算错,比如上图中节点4的深度是3,如果按后序遍历求高度来算,会得到深度是2(但深度实际是3,高度才是2)
在104.二叉树的最大深度
中,如果真正求取二叉树的最大深度,Go
代码应该写成如下:(前序遍历)
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func maxDepth(root *TreeNode) int {if root == nil {return 0}// 使用前序遍历,不断更新最大深度的变量值res := 1dfs(root,1,&res)return res
}func dfs(root *TreeNode,depth int,res *int) {if *res < depth { // 中*res = depth}if root.Left != nil { // 左depth += 1 // 深度+1dfs(root.Left,depth,res)depth -= 1 // 回溯,深度-1}if root.Right != nil { // 右depth += 1 // 深度+1dfs(root.Right,depth,res)depth -= 1 // 回溯,深度-1}}
可以看出使用了前序(中左右)的遍历顺序,这才是真正求深度的逻辑!
注意以上代码是为了把细节体现出来,简化一下代码如下:
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func maxDepth(root *TreeNode) int {if root == nil {return 0}// 使用前序遍历,不断更新最大深度的变量值res := 1dfs(root,1,&res)return res
}func dfs(root *TreeNode,depth int,res *int) {if *res < depth { // 中*res = depth}if root.Left != nil { // 左dfs(root.Left,depth + 1,res)}if root.Right != nil { // 右dfs(root.Right,depth + 1,res)}}
本题思路
递归
此时大家应该明白了既然要求比较高度,必然是要后序遍历
。
递归三步曲分析:
明确递归函数的参数和返回值
参数:当前传入节点。 返回值:当前节点为根的树是否是平衡二叉树
func isBalanced(root *TreeNode) bool {}
明确递归终止条件
递归的过程中依然是遇到空节点了为终止,返回true,表示空节点可以当成平衡二叉树。另一个递归终止条件时,当前树已经明确不是平衡二叉树的时候,没必要继续往后继续递归了,而是应该往上层调用栈回归了。
代码如下:
if root == nil {return true}// 左右子树高度绝对值相差大于1可以直接返回false了if int(math.Abs(float64(getDepth(root.Left) - getDepth(root.Right)))) > 1 {return false}
明确单层递归的逻辑
当前根节点的左子树和右子树均为平衡二叉树
代码如下:
left := isBalanced(root.Left) // 左right := isBalanced(root.Right) // 右res := left && right // 根
最后本题整体递归代码如下:
Go代码
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func isBalanced(root *TreeNode) bool {// 以每一个节点作为根节点,判断他的左右子树高度之差是否小于等于1if root == nil {return true}// 左右子树高度绝对值相差大于1可以直接返回false了if int(math.Abs(float64(getDepth(root.Left) - getDepth(root.Right)))) > 1 {return false}left := isBalanced(root.Left) // 左right := isBalanced(root.Right) // 右res := left && right // 根return res
}// 求二叉树最大深度的模板代码
func getDepth(root *TreeNode) int{if root == nil {return 0}return max(getDepth(root.Left),getDepth(root.Right)) + 1
}func max(a,b int) int {if a > b {return a}return b
}