二叉树经典例题

第 1 题

题目:下列关键字序列为堆的是:() A. 100,60,70,50,32,65 B. 60,70,65,50,32,100 C. 65,100,70,32,50,60 D. 70,65,100,32,50,60 E. 32,50,100,70,65,60 F. 50,100,70,65,60,32

解析 : 堆的本质是满足特定性质的完全二叉树,分为大顶堆(每个父节点值 ≥ 左右子节点值)和小顶堆(每个父节点值 ≤ 左右子节点值)。数组下标从 0 开始时,下标i的左孩子下标为2i+1,右孩子下标为2i+2,只需逐个验证所有非叶子节点是否满足堆性质。

  • 选项 A :序列100,60,70,50,32,65

    • 根节点 100:左孩子 60、右孩子 70,100 ≥ 两者,满足大顶堆;
    • 节点 60:左孩子 50、右孩子 32,60 ≥ 两者,满足;
    • 节点 70:左孩子 65,70 ≥ 65,满足。 所有节点均满足大顶堆规则,是合法的堆。
  • 选项 B:根节点 60 <左孩子 70,不满足大顶堆;节点 70> 子节点 50,也不满足小顶堆,不是堆。

  • 选项 C:根节点 65 <左孩子 100,不满足大顶堆;节点 100> 子节点 32,也不满足小顶堆,不是堆。

  • 选项 D:根节点 70 <右孩子 100,不满足大顶堆;节点 70> 左孩子 65,也不满足小顶堆,不是堆。

  • 选项 E:节点 100 > 左孩子 60,不满足小顶堆,不是堆。

  • 选项 F:根节点 50 <左孩子 100,不满足大顶堆;节点 100> 子节点 65,也不满足小顶堆,不是堆。

答案:A


第 2 题

题目:已知小根堆为 8,15,10,21,34,16,12,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次数是()。 A. 1 B. 2 C. 3 D. 4

解析: 小顶堆删除堆顶的标准流程:

  1. 用堆的最后一个元素替换堆顶元素,堆的元素总数减 1;
  2. 对新堆顶执行向下筛选:每到一个节点,先比较它的所有子节点选出最小值,再将父节点与这个最小值比较,若父节点更大则交换,重复直到满足小顶堆性质。

初始小根堆:[8,15,10,21,34,16,12](共 7 个元素,下标 0~6)

  1. 替换堆顶:删除堆顶 8,将最后一个元素 12 移到堆顶,得到临时序列[12,15,10,21,34,16]
  2. 向下调整并统计比较次数:
    • 第 1 次比较:节点 12 的左孩子 15、右孩子 10,比较两个子节点,选出更小的 10;
    • 第 2 次比较:父节点 12 与最小子节点 10 比较,12 > 10,交换两者,序列变为[10,15,12,21,34,16]
    • 第 3 次比较:被交换下去的 12(下标 2)只有左孩子 16,比较 12 和 16,12 < 16,满足小顶堆,调整结束。

总计 3 次比较。

答案:C


第 3 题

题目:一组记录排序码为 (5 11 7 2 3 17),则利用堆排序方法建立的初始堆为 A. (11 5 7 2 3 17) B. (11 5 7 2 17 3) C. (17 11 7 2 3 5) D. (17 11 7 5 3 2) E. (17 7 11 3 5 2) F. (17 7 11 3 2 5)

解析 : 堆排序默认建立大顶堆,建堆规则:从最后一个非叶子节点开始,从下往上、从右往左逐个调整子树,使每个子树都满足大顶堆。 初始序列[5,11,7,2,3,17]共 6 个元素,最后一个非叶子节点下标为 2(值为 7)。

  1. 调整下标 2(值 7):左孩子是下标 5(值 17),7 < 17,交换后序列:[5,11,17,2,3,7]
  2. 调整下标 1(值 11):左孩子 2、右孩子 3,最大子节点为 3,11 > 3,无需交换;
  3. 调整下标 0(值 5):左孩子 11、右孩子 17,最大子节点为 17,5 < 17,交换后序列:[17,11,5,2,3,7]; 被交换下去的 5(下标 2)继续向下调整:左孩子是 7,5 < 7,交换后序列:[17,11,7,2,3,5]

最终初始大顶堆为(17 11 7 2 3 5)

答案:C


第 4 题

题目:最小堆 0,3,2,5,7,4,6,8,在删除堆顶元素 0 之后,其结果是() A. 3, 2, 5, 7, 4, 6, 8 B. 2, 3, 5, 7, 4, 6, 8 C. 2, 3, 4, 5, 7, 8, 6 D. 2, 3, 4, 5, 6, 7, 8

解析: 小顶堆删除堆顶的操作:末尾元素替换堆顶 → 向下调整至满足小顶堆性质。

初始最小堆:[0,3,2,5,7,4,6,8](共 8 个元素,下标 0~7)

  1. 替换堆顶:删除堆顶 0,将最后一个元素 8 移到堆顶,得到临时序列[8,3,2,5,7,4,6]
  2. 向下调整:
    • 节点 8(下标 0):左孩子 3、右孩子 2,最小子节点为 2;8 > 2,交换后序列:[2,3,8,5,7,4,6]
    • 节点 8(下标 2):左孩子 4、右孩子 6,最小子节点为 4;8 > 4,交换后序列:[2,3,4,5,7,8,6]
    • 节点 8(下标 5)无有效子节点,调整结束。

最终结果为[2, 3, 4, 5, 7, 8, 6]

答案:C

第 5 题

描述

编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中"#"表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。

输入描述:

输入包括1行字符串,长度不超过100。

输出描述:

可能有多组测试数据,对于每组数据, 输出将输入字符串建立二叉树后中序遍历的序列,每个字符后面都有一个空格。 每个输出结果占一行。

示例1
复制代码
abc##de#g##f###
c b e g d f a 

#include <stdio.h>
#include <stdlib.h>

// 二叉树结点结构体:数据域 + 左右孩子指针
typedef struct BiTNode {
    char data;
    struct BiTNode* lchild, *rchild;
} BiTNode, *BiTree;

// 全局下标:记录当前读取到字符串的第几个字符
int indx;

// 递归:按照先序序列创建二叉树,'#' 表示空树
void CreateBiTree(BiTree* T, char str[]) {
    char ch = str[indx++];
    if (ch == '#') {
        *T = NULL; // 空结点,不分配内存
    } else {
        *T = (BiTree)malloc(sizeof(BiTNode));
        (*T)->data = ch;
        CreateBiTree(&((*T)->lchild), str); // 递归建左子树
        CreateBiTree(&((*T)->rchild), str); // 递归建右子树
    }
}

// 中序遍历:左 → 根 → 右,每个字符后带一个空格
void InOrder(BiTree T) {
    if (T != NULL) {
        InOrder(T->lchild);
        printf("%c ", T->data);
        InOrder(T->rchild);
    }
}

int main() {
    char s[100];
    // 多组测试数据:和你图里的 while(scanf...) 框架逻辑完全一致
    while (scanf("%s", s) != EOF) {
        indx = 0;          // 【关键】每组用例必须重置下标,从字符串开头重新读
        BiTree root;
        CreateBiTree(&root, s); // 建二叉树
        InOrder(root);          // 中序遍历输出
        printf("\n");           // 每组结果占一行
    }
    return 0;
}

第 6 题

给定一个二叉树,判断它是否是 平衡二叉树

复制代码
// 辅助函数:返回树的高度;若子树不平衡,返回 -1 作为标记
int getHeight(struct TreeNode* root) {
    // 空树高度为 0
    if (root == NULL) {
        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
    return (leftHeight > rightHeight ? leftHeight : rightHeight) + 1;
}

bool isBalanced(struct TreeNode* root) {
    // 只要返回值不是 -1,就说明整棵树是平衡的
    return getHeight(root) != -1;
}

我们用两棵具体的二叉树 + 逐步骤递归拆解的方式,把代码的执行过程完整走一遍

先再明确一次核心规则(完全对应代码逻辑):

  1. 空节点:高度 = 0,天然平衡
  2. 对任意非空节点,遵循「左 → 右 → 根」的后序顺序:
    • 先算左子树高度 leftHeight
    • 如果左子树返回 -1,说明左子树已经不平衡,当前节点直接返回 -1(剪枝,不用再算右边)
    • 再算右子树高度 rightHeight
    • 如果右子树返回 -1,当前节点直接返回 -1
    • 左右都平衡:计算高度差的绝对值
      • 高度差 > 1 → 不平衡,返回 -1
      • 高度差 ≤ 1 → 平衡,返回 max(左高, 右高) + 1(当前节点的高度)
示例一:平衡二叉树

我们用这棵经典平衡树来走完整流程:

复制代码
        3  ← 根节点
       / \
      9  20
        /  \
       15   7

完整执行步骤:调用 getHeight(3)

进入根节点 3

非空,先处理左子树 → 调用 getHeight(9)

进入节点 9

非空,先处理左子树 → 调用 getHeight(9的左孩子)

左孩子是空节点 → 直接返回 0

leftHeight = 0,不是 - 1,继续

处理右子树 → 调用 getHeight(9的右孩子)

右孩子是空节点 → 直接返回 0

rightHeight = 0,不是 - 1

计算高度差:|0 - 0| = 0 ≤ 1 → 平衡

返回当前高度:max(0,0) + 1 = 1

✅ 节点 9 处理完成,向上返回高度 1

回到根节点 3

leftHeight = 1,不是 - 1,继续处理右子树 → 调用 getHeight(20)

进入节点 20

非空,先处理左子树 → 调用 getHeight(15)

节点 15 左右都是空,和节点 9 逻辑完全一样

最终返回高度 1

leftHeight = 1,不是 - 1,继续

处理右子树 → 调用 getHeight(7)

节点 7 左右都是空,同样返回高度 1

rightHeight = 1,不是 - 1

计算高度差:|1 - 1| = 0 ≤ 1 → 平衡

返回当前高度:max(1,1) + 1 = 2

✅ 节点 20 处理完成,向上返回高度 2

回到根节点 3

rightHeight = 2

计算高度差:|1 - 2| = 1 ≤ 1 → 平衡

返回当前高度:max(1,2) + 1 = 3

最终判断

getHeight(根) 返回 3,不是 -1 → isBalanced 返回 true,这是一棵平衡二叉树。

示例二:不平衡二叉树

我们用这棵 "左斜树" 演示不平衡的判定过程:

复制代码
        1  ← 根节点
       /
      2
     /
    3

完整执行步骤:调用 getHeight(1)

  1. 进入根节点 1 非空,先处理左子树 → 调用 getHeight(2)

  2. 进入节点 2 非空,先处理左子树 → 调用 getHeight(3)

  3. 进入节点 3 左右孩子都是空 高度差为 0,平衡 返回高度:max(0,0) + 1 = 1

    ✅ 节点 3 处理完成,返回高度 1

  4. 回到节点 2 leftHeight = 1,不是 - 1 处理右子树 → 调用 getHeight(2的右孩子)

    • 右孩子是空 → 返回 0 rightHeight = 0 计算高度差:|1 - 0| = 1 ≤ 1 → 平衡 返回当前高度:max(1,0) + 1 = 2

    ✅ 节点 2 处理完成,返回高度 2

  5. 回到根节点 1 leftHeight = 2,不是 - 1 处理右子树 → 调用 getHeight(1的右孩子)

    • 右孩子是空 → 返回 0 rightHeight = 0 计算高度差:|2 - 0| = 2 > 1 → ❌ 不平衡! 直接返回标记 -1

最终判断

getHeight(根) 返回 -1isBalanced 返回 false,这不是平衡二叉树。

示例三:子树先不平衡,提前剪枝

这是最能体现 "自底向上" 优势的场景:子树已经不平衡了,上层就不用再计算了。

复制代码
        1
       / \
      2   3
     /
    4
   /
  5

关键流程

递归到最底层节点 5 → 返回高度 1

节点 4:左高 1,右高 0 → 差 1,平衡,返回高度 2

节点 2:左高 2,右高 0 → 差 2 > 1 → 不平衡,返回 -1

回到根节点 1:拿到左子树返回值 -1

→ 触发代码里 if (leftHeight == -1) return -1;

→ 直接返回 -1,完全不用再去计算右子树 3 的高度

这就是 "剪枝":只要发现某一棵子树不平衡,就立刻把 -1 一路传上去,整条路径都不用再做多余计算,时间复杂度降到 O (n)。

和代码的对应关系
代码逻辑 对应作用
if (root == NULL) return 0; 空节点高度为 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; 当前节点高度差超限,标记不平衡
return max(左,右) + 1; 当前节点平衡,返回自身高度
abs函数

abs 是 C 语言标准库中的函数,全称 absolute value,作用是计算一个整数的绝对值。

1. 基本用法

头文件:#include <stdlib.h>

函数原型:int abs(int x);

功能:传入一个整数 x,返回它的绝对值(非负值)。

举几个简单例子:

复制代码
abs(5);    // 返回 5
abs(-3);   // 返回 3
abs(0);    // 返回 0
abs(-100); // 返回 100
2. 在平衡二叉树代码里的作用

平衡二叉树的判定规则是:左右子树高度差的绝对值 ≤ 1。

我们不关心 "左子树更高" 还是 "右子树更高",只关心两者相差多少。

如果不用 abs,你需要写成:

复制代码
if (leftHeight - rightHeight > 1 || rightHeight - leftHeight > 1) {
    // 不平衡
}

用 abs 之后可以简化成一行:

cpp 复制代码
if (abs(leftHeight - rightHeight) > 1) {
    // 不平衡
}

无论结果是正还是负,取绝对值后都能统一判断差值是否超过 1,代码更简洁直观。

3. 补充说明

abs 只处理 int 类型的整数。

如果要计算浮点数的绝对值,需要用 fabs 函数,头文件是 <math.h>。

对于 long、long long 类型,对应有 labs、llabs 函数,用法完全一致。

相关推荐
wuyk5553 小时前
3.链表:用指针串联的动态数据结构
c语言·开发语言·数据结构·链表
郝学胜-神的一滴3 小时前
干货版《算法导论》17:二叉树核心原理、遍历逻辑与高阶实操全解
数据结构·c++·python·算法·计算机·编程
爱编程的小新☆3 小时前
【LeetCode】从递归到 Flood Fill:5 道题吃透 DFS 的选择、回溯与标记
java·算法·leetcode·深度优先·回溯·flood fill
Water_Sunzhipeng3 小时前
2024牛客暑期多校训练营1
算法
hetao17338374 小时前
2026-08-09~08-14 hetao1733837 的刷题记录
c++·算法
技术小黑4 小时前
RNN算法实战系列06 | LSTM 实现糖尿病探索与预测
rnn·算法·lstm
疯狂打码的少年4 小时前
【数据结构】二叉树的性质(五大性质+计算)
数据结构·笔记·算法
wabs6664 小时前
关于图论【A*算法 | 卡码网127.骑士的攻击的思考】
数据结构·算法·图论·卡码网·广搜的改进版
CIO_Alliance5 小时前
AI认知系列(3)| 数据、算法、算力、场景四要素协同
人工智能·算法·ipaas·系统集成·企业cio联盟·企业级ai化转型
凉茶钱5 小时前
【数据结构】堆的应用
c语言·数据结构