Leetcode-110:判定平衡二叉树

剑指 Offer 55 - II. 平衡二叉树
题目描述:

给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1

题目来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/balanced-binary-tree

首先需要明确的一点是平衡二叉树的定义:树里面每个节点的左右子树的高度差不大于1
碰到这道题我们大多数情况下首先想到的必然是从递归入手。那么对于这个问题如何递归呢?前面有道题是求二叉树的最大深度的,这两者之间的递归思路其实有着很大的相似性,因为我们要确定节点的平衡也是要通过求解每个节点的左右子树的高度来入手的,这里我们需要区分一下树的节点的深度和高度。深度一般是从上到下,而高度是从下到上。

1.自顶向下递归(O(N),O(N))——类似于先序遍历

下面我们先介绍以下最容易想到的递归,我们根据题设条件先实现一个求每个节点的高度的函数getHeight,其实现如下,看起来与前面求解最大深度的递归方法十分类似,不过此时的高度从上到下递减。

    int getHeight(TreeNode * root){
        // 节点为空,高度为0
        if(root == nullptr) return 0;
        // 否则高度就是左右子节点的高度的较大值加一
        else return max(getHeight(root->left),getHeight(root->right))+1;
    }

既然有了求解高度的功能实现,那么我们利用他来进行平衡的判断,我们知道成为一个平衡树需要满足两个条件

  1. 左右子树高度差不大于1
  2. 左右子树也是平衡树
    根据这两个条件我们就可以写出如下递归逻辑的代码,我们可以看到递归的终止条件是节点为空,单层逻辑是判断上述的条件1和2,其中的2也就是我们实现的递归过程。这个递归判断是从判断根节点的平衡依次递到下面的子节点的平衡,然后再归于根节点。这就是一个自顶向下的过程,但是由于对于每个节点都要执行多次高度计算,导致了一些计算冗余,其时间复杂度达到了O(N2)
bool isBalanced(TreeNode* root) {
        // 节点为空,必为平衡节点
        if(root == nullptr)  return true;
        // 否则只有其左右子树同时为平衡树,且左右子树高度差不大于一才是平衡树
        else {
            return abs(getHeight(root->left)-getHeight(root->right)) <= 1 && isBalanced(root->left) &&isBalanced(root->right);
    }
        }

2.自底向上递归((O(N2),O(N))——类似于后序遍历

上面的递归过程中多次求解了高度,导致了极大的计算冗余,是否可以想办法进行消除呢?我们分析一下其递归过程。每次判断节点是否是平衡的时候,需要先计算左右子树高度差,再进行左右子树是否是平衡树的判断,这相当于一个先序遍历的逻辑(先处理根节点再处理子节点)。因为每次处理一个节点都需要处理其下面的所有节点这就造成了冗余,那么如果我们从下到上处理节点会怎样呢?是否会消除这些重复的计算。

基于这一分析我们转变递归逻辑为后序遍历(先处理子节点,再处理根节点)。基于题意,两个判断条件不会改变。所以我们的递归单层逻辑还是判断左右子树高度差以及左右子树是否是平衡树。但是由于此时是自底向上的逻辑,所以每次我们处理到一个节点的时候其左右子树是否是平衡树已经被标记出,不用再冗余计算。所以我们实现的新的辅助函数如下。

注意其中的-1正是我们用来消除冗余的工具。当一个节点的高度标记为-1就表示这个节点不是一个平衡节点,其上方的节点则也不可能是平衡节点。否则getHeight求的值就是节点本身的高度。我们可以得出,只要一个节点的getHeight返回值不是-1,那么这个节点就是一个平衡节点。

//辅助函数,返回节点的高度
//注意下面返回-1表示该节点的左右子树高度差大于一,表示不可能是平衡树
int getHeight(TreeNode * root){
    //当前节点为空,则其高度为0
    if(root == nullptr) return 0;
    //求左子树高度
    int leftHeight = getHeight(root->left);
    if(leftHeight == -1)    return -1;
    //求右子树高度
    int rightHeight = getHeight(root->right);
    if(rightHeight == -1)   return -1;
    if(abs(leftHeight - rightHeight) > 1) return -1;
    //节点高度为左右子树中较高的高度+1
    else return leftHeight > rightHeight ? leftHeight+1:rightHeight+1;
}

然后我们可以写出平衡树判断代码如下

 bool isBalanced(TreeNode* root) {
        // 当前节点的高度正常则说明是平衡树
        // 这里正常的定义就是不返回-1
        return getHeight(root) > -1;
    }

最后附上总体代码

//自底向上递归解决(O(N),O(N))——类似于先序遍历
class Solution {
public:
    //辅助函数,返回节点的高度
    //注意下面返回-1表示该节点的左右子树高度差大于一,表示不可能是平衡树
    int getHeight(TreeNode * root){
	    //当前节点为空,则其高度为0
        if(root == nullptr) return 0;
	    //求左子树高度
        int leftHeight = getHeight(root->left);
        if(leftHeight == -1)    return -1;
	    //求右子树高度
        int rightHeight = getHeight(root->right);
        if(rightHeight == -1)   return -1;
        if(abs(leftHeight - rightHeight) > 1) return -1;
	    //节点高度为左右子树中较高的高度+1
        else return leftHeight > rightHeight ? leftHeight+1:rightHeight+1;
    }
    bool isBalanced(TreeNode* root) {
        // 当前节点的高度正常则说明是平衡树
        // 这里正常的定义就是不返回-1
        return getHeight(root) > -1;
    }
};

//自顶向下递归解决(O(N^2),O(N))——类似于后序遍历
class Solution {
public:
    // 求出当前节点的高度
    int getHeight(TreeNode * root){
        // 节点为空,高度为0
        if(root == nullptr) return 0;
        // 否则高度就是左右子节点的高度的较大值加一
        else return max(getHeight(root->left),getHeight(root->right))+1;
    }
    bool isBalanced(TreeNode* root) {
        // 节点为空,必为平衡节点
        if(root == nullptr)  return true;
        // 否则只有其左右子树同时为平衡树,且左右子树高度差不大于一才是平衡树
        else {
            return abs(getHeight(root->left)-getHeight(root->right)) <= 1 && isBalanced(root->left) &&isBalanced(root->right);
    }
        }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值