本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1
从顶至底(暴力法):
暴力法的思想就是求出左右子树的高度,然后比较左子树的高度和右子树的高度是否符合题意,如果符合,再去判断左子树和右子树是否为平衡二叉树。此法复杂度较高,因为是从顶到底调用求高度函数,其实在求root节点高度时,整个树的高度已经计算出来了但并未保存,每次调用都会带来求此节点为根的子树的重复操作。
height函数用来求节点的高度。
isbalanced函数用来判断以该节点为根的子树是否为平衡二叉树。
int height(TreeNode* root){
if(!root)return 0;
return max(height(root->left),height(root->right))+1; // 根节点的高度 = 左子树和右子树高度的较大值+1
}
bool isBalanced(TreeNode* root){
if(!root)return true;
int h1 = height(root->left),h2 = height(root->right);
return abs(h1-h2) <= 1 && isBalanced(root->left) && isBalanced(root->right);
// 判断左子树和右子树的高度是否符合平衡二叉树 并且 左右子树是否为平衡二叉树 三者皆为true 以root为根的子树为平衡二叉树
}
时间复杂度为O(nlogn)。
从底至顶(提前阻断):
思路 就是利用二叉树的后序遍历来做该题。
二叉树的常用遍历方法一共有四种:
前序遍历:先访问根节点,然后访问左子树,最后访问右子树。
中序遍历:先访问左子树,然后访问根节点,最后访问右子树。
后序遍历:先访问左子树,然后访问右子树,最后访问根节点。
层序遍历:从上到下,从左至右访问某一层的节点。
思考: 使用自底向上改用什么遍历来解决该问题。首先排除到前序遍历和层序遍历 因为它们都是自上而下进行访问的。
再看中序遍历,先左子树,在根节点。 若此时访问到根节点,无法知道右子树的高度,无法解决此问题。所以采用后序遍历。
思路:先通过遍历左子树到达最左端,在调用一次访问空节点,此时返回高度0;再去遍历该节点右子树的根节点,通过后序遍历可以得知该节点右子树的高度,此时对高度进行判断是否符合平衡二叉树的要求,再更新该节点的高度即可。
第一版本:
bool balanced(TreeNode* root,int &height){// 相对于前面的方法加了高度的参数
if(!root){
height = 0;
return true;
}
bool b1,b2;
int h1,h2;
b1 = balanced(root->left,h1);
b2 = balanced(root->right,h2);// 后序遍历
height = max(h1,h2)+1;// 更新该节点高度
return b1 &&b2 &&abs(h1-h2) <2;
}
bool isBalanced(TreeNode* root) {
int h = 0; // 这里必须声明一个变量,因为我使用了引用 直接调用 balanced(root,0)会报错。
return balanced(root,h);
}
第二版本:
int balanced(TreeNode* root){//相对于第一版本 我用返回值int为该节点的高度值 采用-1表示该节点所在的子树不为平衡二叉树 0表示空节点
if(!root)return 0;
int h1 = balanced(root->left);
int h2 = balanced(root->right);
if(h1 == -1 || h2 == -1 || abs(h2-h1) > 1)return -1;// 如果左右子树有不为平衡二叉树的 高度差大于一则返回-1 表示该树已经不为平衡二叉树了 直接返回-1即可 不需要多余判断。
return max(h1,h2) +1; // 返回根节点的高度
}
bool isBalanced(TreeNode* root){
return balanced(root) != -1; // 如果根节点的值为-1 表示不为平衡二叉树 返回false 如果不为-1,则为root的高度 返回true即可
}