本文记录求解二叉树深度的方法及其衍生的相关问题,包括二叉树最小深度,直径、平衡二叉树,其中主要以递归为主要实现方法,关键是考虑对根结点做哪些操作,然后左右子树就构成两个相同的递归子问题。
(一)二叉树的最大深度
题目(Easy):104. 二叉树的最大深度
题目描述:
给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最大深度 3 。
解题思路:
关于二叉树的题目,递归都是最简单的解答方案,我们只需要想清楚对根结点做哪些操作,然后左右子树就构成两个相同的递归子问题。
但是,很多时候非递归代码实现也是需要掌握的,特别是一些比较简单的操作,如二叉树的遍历,求深度等等。非递归操作需要借助栈和队列等数据结构来实现。
代码实现:
//递归解法
class Solution {
public int maxDepth(TreeNode root) {
if(root==null)
return 0;
return 1+Math.max(maxDepth(root.left),maxDepth(root.right));
}
}
//非递归解法,这里我们用DFS,(BFS也可以,记录有多少层即可)
//实际是反前序遍历:根、右、左
class Solution {
class Pair{
TreeNode node;
int height;
public Pair(TreeNode node,int height){
this.node=node;
this.height=height;
}
}
public int maxDepth(TreeNode root) {
if(root==null)
return 0;
Stack<Pair> stack=new Stack<>();
stack.push(new Pair(root,1));
int depth=0;
while(!stack.isEmpty()){
Pair temp=stack.pop();
root=temp.node;
int currentDepth=temp.height;
depth=Math.max(depth,currentDepth); //记录最大深度
if(root.left!=null)
stack.push(new Pair(root.left,currentDepth+1));
if(root.right!=null)
stack.push(new Pair(root.right,currentDepth+1));
}
return depth;
}
}
(二)二叉树的最小深度
题目(Easy):111. 二叉树的最小深度
题目描述:
给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最小深度 2.
解题思路:
本题和上题基本类似,所不同的是这里求的是最小深度,而我们很容易想到最小深度等于1+左右子树深度的较小值,但是实际上还有一种特殊情况,当二叉树为斜二叉树时,其子树有一边深度为0,但是这并不符合深度(根到叶子)的定义,因此需要特别注意这种情况。
代码实现:
//只给出递归解法,非递归和上题基本类似,只需要维护一个较小值就可以了
class Solution {
public int minDepth(TreeNode root) {
if(root==null)
return 0;
int left=minDepth(root.left);
int right=minDepth(root.right);
if(root.left==null || root.right==null) //斜二叉树,有一边是0
return left+right+1;
return 1+Math.min(left,right);
}
}
(三)二叉树的直径
题目(Easy):543. 二叉树的直径
题目描述:
给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
示例 :
给定二叉树:
1
/
2 3
/
4 5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
解题思路:
本题要求求任意两个结点之间长度的最大值,最开始,认为该最大值应当是左子树的最大深度+根结点+右子树的最大深度,但是按照思路提交代码后,发现无法通过,仔细考虑,可以发现其实两个结点间的最大长度不一定会经过根,比如在下方我们给出一个特例:
有了这个实例,就可以发现其实直径仍然应该是左子树最大深度穿过某个结点加上右子树的最大深度,只不过该节点并不一定是我们整棵树的根,因此需要在遍历过程中记录,所以本题仍然是二叉树的深度的一个变形应用。
代码实现:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
//求二叉树深度的一个变种问题
//需要注意:直径不一定经过根,重点是任意一个结点,都要记录以此结点为根的直径情况:左子树高度+右子树高度
int res;
public int diameterOfBinaryTree(TreeNode root) {
if(root==null)
return 0;
int depth=getDepth(root);
return res-1; //边数比结点数少1
}
public int getDepth(TreeNode root){ //求二叉树的深度
if(root==null)
return 0;
int left=getDepth(root.left);
int right=getDepth(root.right);
res=Math.max(res,left+right+1); //在求深度的过程中依次记录了以每个结点为根的直径最大值
return 1+Math.max(left,right);
}
}
(四)平衡二叉树
题目:110. 平衡二叉树
题目描述:
给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例 :
给定二叉树 [3,9,20,null,null,15,7]
3
/
9 20
/
15 7
返回true
解题思路:
此题的目的在于判断每个结点左右子树之差,实际上仍然是求二叉树深度的一个变形问题。具体分析参考:【剑指Offer】39、平衡二叉树
代码实现:
class Solution {
public boolean isBalanced(TreeNode root) {
if(root==null)
return true;
int res=getDepth(root);
return res==-1?false:true;
}
public int getDepth(TreeNode root){
if(root==null)
return 0;
int left=getDepth(root.left);
if(left==-1)
return -1;
int right=getDepth(root.right);
if(right==-1)
return -1;
if(Math.abs(left-right)>1)
return -1;
return 1+Math.max(left,right);
}
}