IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Leetcode-110:判定平衡二叉树 -> 正文阅读

[数据结构与算法]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);
    }
        }
};
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-05-18 17:54:13  更:2022-05-18 17:56:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/30 2:06:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码