C语言平衡二叉树真题练习

目录
  • 一、题目描述
  • 二、解题思路
    • 自顶向下的递归(暴力解法)
    • 自底向上的递归(最优解法)

题目难度:简单

LeetCode链接:平衡二叉树

一、题目描述

给定一个二叉树,判断它是否是高度平衡的二叉树。

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

二、解题思路

一棵二叉树是平衡二叉树,当且仅当其所有子树也都是平衡二叉树,因此我们使用递归的方式依次判断其所有子树是否为平衡二叉树,就知道这棵二叉树是不是平衡二叉树了。

自顶向下的递归(暴力解法)

自顶向下类似于 前序遍历,先判断当前树是否平衡,再判断当前树的左右子树是否平衡。

核心思路

写两个函数:

子函数:计算当前任意一个节点(树) root 的高度 root 是空节点:Depth ( root ) = 0root 是非空节点:Depth ( root ) = max ( Depth ( root->left ) , Depth ( root->right ) ) + 1

主函数:依次递归遍历完 root 的所有子树,对于「当前遍历到的子树」,判断是否平衡,首先计算其左右子树的高度,然后判断高度差是否不超过 1

  • 如果不超过,才能继续往下递归遍历「当前树的左右子树」,判断其是否平衡;
  • 如果超过1,说明不满足平衡条件,则直接返回 false,不用往下递归了。

递归过程演示:自顶向下的递归类似于前序遍历

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
// 计算当前任意一个节点(树)的高度(子函数)
int TreeDepth(struct TreeNode* root)
{
    // 当前节点为空
    if(root == NULL)
    {
        return 0;
    }
    // 当前节点不为空,分别计算它的左右子树的高度
    int leftDepth = TreeDepth(root->left);
    int rightDepth = TreeDepth(root->right);
    // 当前节点(树)的高度
    return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}
bool isBalanced(struct TreeNode* root){
    // 依次递归遍历完root的所有子树,分别判断当前子树是否为高度平衡二叉树
    // 当前树的根节点为空,说明其满足高度平衡的二叉树,返回true
    if(root == NULL)
    {
        return true;
    }
    // 当前树的根节点不为空,分别计算它左右子树的高度
    int leftDepth = TreeDepth(root->left);
    int rightDepth = TreeDepth(root->right);
    // 计算左右子树的高度差
    int ret = leftDepth > rightDepth ? leftDepth - rightDepth : rightDepth - leftDepth;
    // 高度差不超过1,才能继续往下递归遍历当前树的左右子树
    return ret <= 1 && isBalanced(root->left) && isBalanced(root->right);
}

复杂度分析:

  • 时间复杂度:O(n2),其中 n 是二叉树中的节点个数。

最坏情况下,二叉树是满二叉树,主函数 isBalanced(root) 需要遍历二叉树中的所有节点,时间复杂度是 O(n)。计算每个子树的最大高度函数 TreeDepth(root) 被重复调用。除了根节点,其余所有节点都会被遍历两次,复杂度为 O[2(n-1)],所以时间复杂度为 n*2(n-1) ≈ n2。

  • 空间复杂度:O(n),其中 n 是二叉树中的节点个数。空间复杂度主要取决于递归调用的层数,递归调用的层数不会超过 n。

自底向上的递归(最优解法)

方法一自顶向下递归,类似 前序遍历,先判断当前树是否平衡,再判断当前树的左右子树是否平衡,所以对于同一个节点,函数 TreeDepth 会被重复调用,会重复计算很多次子树的高度,导致时间复杂度较高。

如果使用自底向上的做法,则对于每个节点,函数 TreeDepth 只会被调用一次。因为到达左子树底部后,每次对应的左子树都是放在递归调度中的,每次只需要获取新的右子树长度便可。

自底向上递归类似于 后序遍历,对于当前遍历到的节点,先递归地判断其左右子树是否平衡,再判断以当前节点为根的子树是否平衡。

  • 如果当前树的左/右子树中只要有一个不平衡,则整个树就不平衡,返回-1(表示不平衡)
  • 如果当前树是平衡的,则返回其高度,否则返回 -1(表示不平衡)。

写递归算法需要关注什么?

  • 整个递归的终止条件:递归应该在什么时候结束?— 子树根节点为空的时候,空树也是平衡二叉树。
  • 本级递归应该做什么? — 判断当前树的左子树、右子树、以及当前树是否是平衡的。
  • 返回值:应该返回给上一级的返回值是什么?— 当前树是平衡的,则返回其高度,不平衡则返回 -1。

递归算法流程:

每一级递归时,在我们眼中,当前树就是这样的,只有 rootleftright 三个节点。

到叶子节点了,当前树根节点 root 为空,说明是平衡的,则返回高度 0;

当前树根节点 root 不为空,计算左右子树 leftright 的高度,并判断:

  • 如果「左子树 left 高度为 -1」、或「右子树 right 高度为 -1」、或「左右子树高度差 > 1」,说明整个树 不平衡 ,直接返回 -1(表示不平衡)。
  • 如果不满足上面 3 种情况,说明当前树是 平衡 的,返回当前树的高度,即 max ( left, right ) + 1

补充:计算绝对值的函数:int abs( int n ); ,头文件 <stdlib.h> or <math.h>。

递归过程演示:自底向上递归类似于后序遍历

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
// 计算当前树的高度,并判断当前树是否是平衡二叉树
int _isBalanced(struct TreeNode* root)
{
    // 先分别判断当前树的左/右子树是否平衡
    // 如果当前树的左/右子树中只要有一个不平衡,则全树就不平衡,返回-1(表示不平衡)
    // 如果当前树的左右子树都平衡,则继续判断当前树是否平衡
    // 1. 到叶子节点了,当前树根节点为空,说明是平衡的,则返回高度0
    if(root == NULL)
    {
        return 0;
    }
    // 2. 当前树根节点不为空
    // 计算左右子树的高度
    int leftTreeDepth = _isBalanced(root->left);
    int rightTreeDepth = _isBalanced(root->right);
    // 不平衡的3种情况:左子树高度为-1,右子树高度为-1,左右子树高度差>1
    if(leftTreeDepth == -1 || rightTreeDepth == -1 || abs(leftTreeDepth - rightTreeDepth) > 1)
    {
        return -1;
    }
    // 如果不满足上面3种情况,说明当前树是平衡的,返回当前树的高度
    return leftTreeDepth > rightTreeDepth ? leftTreeDepth + 1 : rightTreeDepth + 1;
}
bool isBalanced(struct TreeNode* root){
    // 递归遍历过程中:
    // 只要有一个子树高度为-1,说明整个树是不平衡的,返回false
    // 所有子树高度都不等于-1,说明整个树是平衡的,返回true
    return _isBalanced(root) != -1;
}

复杂度分析:

1.时间复杂度:O(n),其中 n 是二叉树中的节点个数。

最坏情况是二叉树为满二叉树时,需要遍历完满二叉树中的所有节点,自底向上方法,因此每个节点只会被遍历一次,所以时间复杂度是 O(n)。

2.空间复杂度:O(n),其中 n 是二叉树中的节点个数。空间复杂度却决于递归调用的层数,有 n 个节点的二叉树为单边树时深度最大,为 n。

到此这篇关于C语言平衡二叉树真题练习的文章就介绍到这了,更多相关C语言平衡二叉树内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 如何使用C语言实现平衡二叉树数据结构算法

    目录 前言 一.平衡二叉树实现原理 二.平衡二叉树实现算法 三.全部代码 前言 对于一个二叉排序树而言 它们的结构都是根据了二叉树的特性从最左子树开始在回到该结点上继续往右结点走 通过该方式进行递归操作,并且该二叉排序树的结构也是从小到大依次显示 那么我们假设a[10]={ 3,2,1,4,5,6,7,10,9,8 };我们需要查找改列表中的某一个结点的值 那么我们通过二叉排序树的展示,会展示成如图: 可以发现,如果我们想通过二叉排序树这个深度为8的树来查找某个数 我们需要走到最后,这是最坏的打

  • C语言平衡二叉树详解

    目录 调整措施: 一.单旋转 二.双旋转 AVL树的删除操作: 删除分为以下几种情况: 1.要删除的节点是当前根节点T. 2.要删除的节点元素值小于当前根节点T值,在左子树中进行删除. 3.要删除的节点元素值大于当前根节点T值,在右子树中进行删除. 总结 平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树.这个方案很好的解决了二叉查找树退化成链表的

  • C语言 数据结构平衡二叉树实例详解

    数据结构平衡二叉树 参考代码如下: /* 名称:平衡二叉树 语言:数据结构C语言版 编译环境:VC++ 6.0 日期: 2014-3-26 */ #include <stdio.h> #include <malloc.h> #include <windows.h> #define LH +1 // 左高 #define EH 0 // 等高 #define RH -1 // 右高 #define N 5 // 数据元素个数 typedef char KeyType; /

  • C语言数据结构之平衡二叉树(AVL树)实现方法示例

    本文实例讲述了C语言数据结构之平衡二叉树(AVL树)实现方法.分享给大家供大家参考,具体如下: AVL树是每个结点的左子树和右子树的高度最多差1的二叉查找树. 要维持这个树,必须在插入和删除的时候都检测是否出现破坏树结构的情况.然后立刻进行调整. 看了好久,网上各种各种的AVL树,千奇百怪. 关键是要理解插入的时候旋转的概念. // // AvlTree.h // HelloWorld // Created by feiyin001 on 17/1/9. // Copyright (c) 201

  • C语言平衡二叉树真题练习

    目录 一.题目描述 二.解题思路 自顶向下的递归(暴力解法) 自底向上的递归(最优解法) 题目难度:简单 LeetCode链接:平衡二叉树 一.题目描述 给定一个二叉树,判断它是否是高度平衡的二叉树. 本题中,一棵高度平衡二叉树定义为:一个二叉树 每个节点 的左右两个子树的高度差的绝对值不超过 1 . 二.解题思路 一棵二叉树是平衡二叉树,当且仅当其所有子树也都是平衡二叉树,因此我们使用递归的方式依次判断其所有子树是否为平衡二叉树,就知道这棵二叉树是不是平衡二叉树了. 自顶向下的递归(暴力解法)

  • C语言实例真题讲解数据结构中单向环形链表

    目录 1.例题引入 2.何为带环链表 3.题解思路 4.拓展问题 目录 1.例题引入 链接直达: 环形链表 题目: 2.何为带环链表 正常的单链表每个节点顺次链接,最后一个节点指向NULL,如下: 而带环链表的最后一个节点不再指向NULL了,指向的是前面任意一个节点,以此形成带环链表,并一直循环下去.如下: 3.题解思路 我们可以将上述图画的抽象一点,在没有进入环之前我们用直线表示,进入环之后用圈来表示,以此示意循环.此题需要用到我们之前讲解的求中间节点和求倒数第k个节点的快慢指针的思想.定义两

  • Go语言切片常考的面试真题解析

    目录 前言 01. 数组和切片有什么区别? 02. 拷贝大切片一定比拷贝小切片代价大吗? 03. 切片的深浅拷贝 04. 零切片.空切片.nil切片是什么 05. 切片的扩容策略 07. 参数传递切片和切片指针有什么区别? 08. range遍历切片有什么要注意的? 总结 前言 哈喽,大家好,我是asong.最近没事在看八股文,总结了几道常考的切片八股文,以问答的方式总结出来,希望对正在面试的你们有用- 本文题目不全,关于切片的面试真题还有哪些?欢迎评论区补充- 01. 数组和切片有什么区别?

  • C语言修炼之路函数篇真题训练下

      本文的Gitee地址:文章源代码 第壹题 :字符串逆序(递归实现) 方法一,非递归实现 main主体部分 数组名是首元素的地址 首元素是char类型,对应的传参元素过去就是  char*  类型 采用两个指针不断移动,然后交换两个位置的元素来实现逆序 方法贰,递归实现 大致思路 代码实现 (推荐自己手动模拟一下) void reverse_string(char* str) { int len = strlen(str); char tmp = str[0]; str[0] = str[le

  • C语言修炼之路函数篇真题训练上

    本文对应文章 : C语言修炼之路一朝函数思习得 模块思维世间生上篇 C语言修炼之路一朝函数思习得 模块思维世间生下篇 第壹题 A选项 C语言的函数每次只能返回一个元素,上面代码中的 return a,b 只能执行逗号表达式的最后一个语句,即返回20 B选项 C选项 D选项 全局变量在整个程序的任意地方都可以使用 第贰题 C选项 函数不可嵌套定义,但可以嵌套调用  --  “上一篇文章中提及过” 第叁题 A选项 可以 return void 不返回任何参数 B选项 正确 C选项 可以使用全局变量

  • C语言经典顺序表真题演练讲解

    目录 1.移除元素 2.删除有序数组中的重复项 3.合并两个有序数组 1.移除元素 链接直达: https://leetcode-cn.com/problems/remove-element/ 题目: 思路: 法一:依次挪动数据进行覆盖 从第一个数据开始进行依次遍历,如同示例1,依次遍历数组,找到移除的元素2就把后面的数据往前挪动进行覆盖,如图所示: 此法有个缺陷,题目中明确指出使用空间复杂度O(1)的方法解决此问题,而此法的空间复杂度刚好为O(1),可以解决,不过考虑周全些,时间复杂度在情况最

  • C语言单值二叉树真题讲解

    目录 一.题目描述 二.解题思路 [OJ - 二叉树]单值二叉树 LeetCode链接:单值二叉树 题目难度:简单 一.题目描述 如果二叉树每个节点都具有相同的值,那么该二叉树就是 单值 二叉树. 只有给定的树是单值二叉树时,才返回 true:否则返回 false. 二.解题思路 二叉树的递归遍历,一般都会把问题拆分成 当前树(根节点) 和 子树,然后子树又进行拆分,来解决问题. 核心思路: 1.先判断当前节点是否为空,如果为空,返回 true(空树也满足单值二叉树的条件) 2.判断当前树是不是

  • C语言利用面试真题理解指针的使用

    目录 前言 笔试题一 笔试题二 笔试题三 笔试题四 笔试题五 笔试题六 笔试题七 笔试题八 前言 大家好~我又来了,今天给大家带来的是指针的几道笔试题,希望能够加强大家对指针知识的把握,指针就应该这样学! 笔试题一 #include<stdio.h> int main() { int a[5] = { 1 , 2 , 3 , 4 , 5 }; int* ptr = (int*) (&a + 1); printf("%d, %d", *(a + 1), *(ptr -

  • java蓝桥杯历年真题及答案整理(小结)

    蓝桥杯java历年真题及答案整理(闭关一个月,呕心沥血整理出来的) 1 全排列 是这样的,如果给定N个不同字符,将这N个字符全排列,最终的结果将会是N!种.如:给定 A.B.C三个不同的字符,则结果为:ABC.ACB.BAC.BCA.CAB.CBA一共3!=3*2=6种情况. package Question1_9; import java.util.Scanner; import java.util.Vector; public class Question1 { public static

  • javascript实现C语言经典程序题

    最近在学习Javascript语言,看到网上很多都是在介绍Javascript如何解决网页上问题的代码,所以想另辟蹊径,用Javascript代码来实现C语言经典程序题.当然,这些C语言程序题也是比较简单,主要想通过Javascript语言实现,起到语法练习作用,也想来对比一下C语言和Javascript语言实现的相同点和不同点,从而巩固记忆,加强学习效果!!! 一.C语言经典程序题1 1. 题目描述: 马克思的手稿中有这样一道有趣的数学题:有30个人,其中有男人,女人,小孩.他们在一家饭馆中吃

随机推荐