剑指offer解题报告(Java版)——二叉树的深度 判断二叉树是否平衡 38
问题一
引言
在之前我们讲到过如果保存树根节点到叶子节点之间的路径,其中包含了如何判断一个节点是否是叶子节点,而且用栈保存了路径中的节点,只要遍历一下栈即可,然后用一个深度保存所有遍历深度中的最大值
但仔细想想,这种方法是否过于繁琐,我们其实不需要一个完整的路径,为什么要去用一个栈保留路径呢,我们仅仅需要的是深度而已,所以我们可以从深度的角度出发去思考问题
分析问题
考虑递归,一棵树的深度一定是左子树和右子树深度较大值加1
左子树如果是空,那就是右子树的深度加1,其实这个情况也包含到了上面的情况中,这种情况中左子树的深度为0,肯定在比较大小中被淘汰了
解决问题
public int treePath(BinaryTreeNode root)
{
if(root==null)
return 0;
int left=treePath(root.lefNode);
int right=treePath(root.rightNode);
return left>right?(left+1):(right+1);
}
问题二
引言
有了问题一的基础,我们很想当然的会想到一个方法,是对于根节点,由之前的treePath得到左右子树的高度,如果左右子树高度差大于1,那么必然不是平衡的,如果高度差不大于1,那么递归遍历左右孩子是否满足根节点的特性
但是这样的方法稍加思考一下就会发现,根节点会去遍历最左下角的节点,根节点的左孩子也会去遍历最左下角的节点,这说明有很多节点被重复遍历了多次,这个跟求斐波拉契数中用到递归的方式很像,大家估计也能想象用这种递归算斐波拉契是多么的慢,所以这是很难忍受的
分析问题
如何规避这种重复遍历某些节点多次呢,我们想到用斐波拉契数求法中保留中间变量的思想,迭代进行,而在二叉树中如何迭代呢,最好的方式就是我们遍历到某一子树根节点的时候已经遍历完它的左右孩子,然后基于这个继续往上爬,也就是说我们从下往上看,而不是从上往下看
这个特性正是二叉树的后序遍历特性,左右根的顺序
解决问题
根据之前的分析,我们在判断一个二叉树是否平衡时,运用后序遍历的思想,先递归到左子树,在递归到右子树,最后回到根节点,每一层都是如此,只有当左右返回都为true时,才考虑根节点的情况
public boolean isBalanced(BinaryTreeNode root) {
if (root == null) {
return true;
}
boolean leftFlag = isBalanced(root.lefNode);
boolean rightFlag = isBalanced(root.rightNode);
if (leftFlag && rightFlag) {
return baseIsBalanced(root);
}
return false;
}
这里考虑根节点的时候用到了一个baseIsBalanced的基础方法,用于判断根节点是否满足平衡性,而这个基础方法中又用到之前我们说的计算树的深度的函数
public int treePath(BinaryTreeNode root) {
if (root == null)
return 0;
int left = treePath(root.lefNode);
int right = treePath(root.rightNode);
return left > right ? (left + 1) : (right + 1);
}
public boolean baseIsBalanced(BinaryTreeNode root) {
int left = treePath(root.lefNode);
int right = treePath(root.rightNode);
if (left - right >= -1 && left - right <= 1) {
return true;
} else {
return false;
}
}