当前位置: 首页 > news >正文

【考研408数据结构-07】 树与二叉树(下):特殊树结构与应用

📚 【考研408数据结构-07】 树与二叉树(下):特殊树结构与应用

🎯 考频:⭐⭐⭐⭐⭐ | 题型:选择题、综合应用题、算法设计题 | 分值:约8-15分

引言

想象你在管理一个大型图书馆,需要快速查找图书、保持书架平衡、压缩存储目录,还要管理不同分馆的归属关系。这些实际问题恰好对应着本文要讲的特殊树结构:二叉查找树用于快速检索,AVL树保持平衡,哈夫曼树实现最优编码,并查集处理集合关系。

在408考试中,特殊树结构是绝对的重点和难点,几乎每年都会出现相关题目。据统计,近5年的408真题中,BST和AVL树出现6次,哈夫曼编码出现4次,并查集出现3次。这些内容不仅在选择题中考察概念,更常出现在算法设计题中。

本文将帮你彻底掌握完全二叉树、BST、AVL树、哈夫曼树和并查集,让你在考场上游刃有余。

学完本文,你将能够:

  1. ✅ 准确判断和构造各种特殊二叉树
  2. ✅ 熟练实现BST和AVL树的操作
  3. ✅ 手工构造哈夫曼树并求编码
  4. ✅ 掌握并查集的优化技巧

一、知识精讲

1.1 完全二叉树与满二叉树

概念定义

满二叉树(Full Binary Tree):深度为k的二叉树有2k−12^k-12k1个节点

完全二叉树(Complete Binary Tree):除最后一层外,每层都是满的,最后一层的节点从左到右连续排列

💡 408考纲要求

  • 掌握:完全二叉树的性质和判定
  • 应用:顺序存储的下标计算
重要性质对比
性质满二叉树完全二叉树
节点数2k−12^k-12k1(k为深度)2k−1≤n≤2k−12^{k-1} \leq n \leq 2^k-12k1n2k1
叶子节点数2k−12^{k-1}2k1⌈n/2⌉\lceil n/2 \rceiln/2
顺序存储无空间浪费几乎无空间浪费
父节点索引⌊i/2⌋\lfloor i/2 \rfloori/2⌊i/2⌋\lfloor i/2 \rfloori/2
左孩子索引2i2i2i2i2i2i(若存在)
右孩子索引2i+12i+12i+12i+12i+12i+1(若存在)

⚠️ 易错点:完全二叉树的最后一层节点必须连续,不能有空缺!

1.2 二叉查找树(BST)

定义与性质

二叉查找树(Binary Search Tree):满足以下性质的二叉树:

  • 左子树所有节点的值 < 根节点的值
  • 右子树所有节点的值 > 根节点的值
  • 左右子树也是BST

时间复杂度

  • 平均情况:O(log n)
  • 最坏情况:O(n)(退化成链表)
核心操作
  1. 查找:类似二分查找
  2. 插入:先查找,再作为叶子节点插入
  3. 删除(重点⭐⭐⭐⭐⭐):
    • 删除叶子节点:直接删除
    • 删除只有一个孩子的节点:用孩子替代
    • 删除有两个孩子的节点:用中序前驱或后继替代

1.3 平衡二叉树(AVL)

核心概念

平衡二叉树(AVL Tree):任何节点的左右子树高度差不超过1的BST

平衡因子(Balance Factor):BF = 左子树高度 - 右子树高度,取值范围{-1, 0, 1}

四种旋转操作 🎯
  1. LL型(右旋):在左孩子的左子树插入
  2. RR型(左旋):在右孩子的右子树插入
  3. LR型(先左旋后右旋):在左孩子的右子树插入
  4. RL型(先右旋后左旋):在右孩子的左子树插入

记忆口诀:“单旋看孩子,双旋看孙子”

1.4 哈夫曼树与编码

基本概念

带权路径长度(WPL)WPL=∑i=1nwi×liWPL = \sum_{i=1}^{n} w_i \times l_iWPL=i=1nwi×li

哈夫曼树:WPL最小的二叉树,也称最优二叉树

构造算法
  1. 将n个权值作为n个只有根节点的树
  2. 选择权值最小的两棵树合并
  3. 新树的权值为两棵树权值之和
  4. 重复步骤2-3,直到只剩一棵树

哈夫曼编码特点

  • 前缀编码(任何字符编码都不是其他字符编码的前缀)
  • 平均编码长度最短

1.5 并查集

基本思想

并查集(Union-Find):用树形结构表示集合,支持:

  • Find:查找元素所属集合
  • Union:合并两个集合
优化技巧
  1. 路径压缩:查找时将路径上的节点直接连到根
  2. 按秩合并:矮树并到高树下

二、代码实现

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>// ========== 二叉查找树(BST)实现 ==========
typedef struct BSTNode {int data;struct BSTNode *left, *right;
} BSTNode;// BST插入操作
BSTNode* BST_Insert(BSTNode* root, int key) {if (root == NULL) {BSTNode* node = (BSTNode*)malloc(sizeof(BSTNode));node->data = key;node->left = node->right = NULL;return node;}if (key < root->data)root->left = BST_Insert(root->left, key);else if (key > root->data)root->right = BST_Insert(root->right, key);return root;
}// BST查找操作
BSTNode* BST_Search(BSTNode* root, int key) {if (root == NULL || root->data == key)return root;if (key < root->data)return BST_Search(root->left, key);elsereturn BST_Search(root->right, key);
}// BST删除操作(重点)
BSTNode* BST_Delete(BSTNode* root, int key) {if (root == NULL) return NULL;if (key < root->data)root->left = BST_Delete(root->left, key);else if (key > root->data)root->right = BST_Delete(root->right, key);else {// 找到要删除的节点if (root->left == NULL) {BSTNode* temp = root->right;free(root);return temp;}else if (root->right == NULL) {BSTNode* temp = root->left;free(root);return temp;}// 有两个孩子:找中序后继(右子树最小值)BSTNode* minNode = root->right;while (minNode->left != NULL)minNode = minNode->left;root->data = minNode->data;root->right = BST_Delete(root->right, minNode->data);}return root;
}// ========== AVL树实现 ==========
typedef struct AVLNode {int data;int height;  // 节点高度struct AVLNode *left, *right;
} AVLNode;// 获取节点高度
int getHeight(AVLNode* node) {return node ? node->height : 0;
}// 更新节点高度
void updateHeight(AVLNode* node) {int leftHeight = getHeight(node->left);int rightHeight = getHeight(node->right);node->height = (leftHeight > rightHeight ? leftHeight : rightHeight) + 1;
}// LL旋转(右旋)
AVLNode* rotateRight(AVLNode* y) {AVLNode* x = y->left;AVLNode* T2 = x->right;x->right = y;y->left = T2;updateHeight(y);updateHeight(x);return x;
}// RR旋转(左旋)
AVLNode* rotateLeft(AVLNode* x) {AVLNode* y = x->right;AVLNode* T2 = y->left;y->left = x;x->right = T2;updateHeight(x);updateHeight(y);return y;
}// AVL插入
AVLNode* AVL_Insert(AVLNode* root, int key) {// 1. 执行BST插入if (root == NULL) {AVLNode* node = (AVLNode*)malloc(sizeof(AVLNode));node->data = key;node->height = 1;node->left = node->right = NULL;return node;}if (key < root->data)root->left = AVL_Insert(root->left, key);else if (key > root->data)root->right = AVL_Insert(root->right, key);elsereturn root;  // 重复值不插入// 2. 更新高度updateHeight(root);// 3. 获取平衡因子int balance = getHeight(root->left) - getHeight(root->right);// 4. 根据失衡类型进行旋转// LL型if (balance > 1 && key < root->left->data)return rotateRight(root);// RR型if (balance < -1 && key > root->right->data)return rotateLeft(root);// LR型if (balance > 1 && key > root->left->data) {root->left = rotateLeft(root->left);return rotateRight(root);}// RL型if (balance < -1 && key < root->right->data) {root->right = rotateRight(root->right);return rotateLeft(root);}return root;
}// ========== 哈夫曼树实现 ==========
typedef struct HuffmanNode {int weight;struct HuffmanNode *left, *right;
} HuffmanNode;// 构造哈夫曼树(简化版,实际需要最小堆)
HuffmanNode* createHuffmanTree(int weights[], int n) {// 这里省略了最小堆的实现// 实际408考试中通常要求手工构造return NULL;
}// ========== 并查集实现 ==========
#define MAX_SIZE 1000int parent[MAX_SIZE];
int rank[MAX_SIZE];  // 秩(树的高度)// 初始化并查集
void initUnionFind(int n) {for (int i = 0; i < n; i++) {parent[i] = i;  // 每个元素的父节点初始为自己rank[i] = 0;}
}// 查找操作(带路径压缩)
int find(int x) {if (parent[x] != x) {parent[x] = find(parent[x]);  // 路径压缩}return parent[x];
}// 合并操作(按秩合并)
void unionSet(int x, int y) {int rootX = find(x);int rootY = find(y);if (rootX == rootY) return;// 按秩合并:矮树并到高树下if (rank[rootX] < rank[rootY]) {parent[rootX] = rootY;} else if (rank[rootX] > rank[rootY]) {parent[rootY] = rootX;} else {parent[rootY] = rootX;rank[rootX]++;}
}// 判断是否在同一集合
bool isConnected(int x, int y) {return find(x) == find(y);
}

复杂度分析

  • BST:平均O(log n),最坏O(n)
  • AVL:插入、删除、查找均为O(log n)
  • 哈夫曼树构造:O(n log n)
  • 并查集:近似O(α(n)),α为反阿克曼函数

三、图解说明

【图1】AVL树的四种旋转

LL型(右旋):y                x/ \              / \x   T3    =>     T1  y/ \                  / \
T1  T2               T2  T3RR型(左旋):x                    y/ \                  / \
T1  y        =>      x   T3/ \              / \T2  T3           T1  T2LR型(先左后右):z                z                 y/                /                 / \x        =>      y        =>       x   z\              /y            xRL型(先右后左):z                z                 y\                \               / \x      =>        y      =>     z   x/                  \y                    x

【图2】哈夫曼树构造过程

权值:{5, 2, 3, 4}Step 1: 选择2和35/ \2   3Step 2: 合并为5,再与4合并9/ \4   5/ \2   3Step 3: 最后与5合并14/  \5    9/ \4   5/ \2   3编码:5:0, 4:10, 2:110, 3:111
WPL = 5×1 + 4×2 + 2×3 + 3×3 = 28

【图3】并查集路径压缩

压缩前:              压缩后:1                    1/                   / | \2                   2  3  4/
3
|
4find(4)执行后,2、3、4直接连到根节点1

四、真题演练

【2023年408真题】

题目:给定序列{4, 5, 7, 2, 1, 3, 6},依次插入初始为空的AVL树,画出最终的AVL树。

解题思路

  1. 依次插入,每次插入后检查平衡
  2. 发生失衡时进行相应旋转

标准答案

        4/ \2   6/ \ / \1  3 5  7

评分要点

  • 正确判断失衡类型(3分)
  • 正确执行旋转操作(3分)
  • 最终树形结构正确(4分)

【2022年408真题】

题目:设有字符集{a, b, c, d},使用频率分别为{7, 5, 2, 4},构造哈夫曼树并给出编码。

解答

  1. 构造哈夫曼树:先合并2和4得6,再合并5和6得11,最后合并7和11得18
  2. 哈夫曼编码:a:0, b:10, d:110, c:111
  3. 平均编码长度:1×7/18 + 2×5/18 + 3×4/18 + 3×2/18 = 1.94

⚠️ 易错点

  • 注意权值相同时的处理顺序
  • 编码时左0右1还是左1右0要统一

【变式题目】

题目:在并查集中执行union(3,4), union(1,2), union(2,3)后,find(4)的返回值是?

解答:执行操作后,1成为所有元素的根,因此find(4)返回1。

五、在线练习推荐

LeetCode相关题目

  • 98. 验证二叉搜索树 - 中等
  • 110. 平衡二叉树 - 简单
  • 1584. 连接所有点的最小费用 - 中等(最小生成树)
  • 547. 省份数量 - 中等(并查集)

练习顺序建议

  1. 先练习BST的基本操作(插入、查找、删除)
  2. 手工模拟AVL树的旋转操作
  3. 大量练习哈夫曼树构造和编码计算
  4. 掌握并查集的模板代码

六、思维导图

中心主题:特殊树结构与应用
├── 一级分支1:完全二叉树
│   ├── 二级分支1.1:定义与性质
│   ├── 二级分支1.2:顺序存储
│   └── 二级分支1.3:堆的基础
├── 一级分支2:二叉查找树BST
│   ├── 二级分支2.1:查找操作
│   ├── 二级分支2.2:插入操作
│   └── 二级分支2.3:删除操作(重点)
├── 一级分支3:平衡二叉树AVL
│   ├── 二级分支3.1:平衡因子
│   ├── 二级分支3.2:四种旋转
│   └── 二级分支3.3:插入调整
├── 一级分支4:哈夫曼树
│   ├── 二级分支4.1:WPL计算
│   ├── 二级分支4.2:构造算法
│   └── 二级分支4.3:哈夫曼编码
└── 一级分支5:并查集├── 二级分支5.1:Find操作├── 二级分支5.2:Union操作└── 二级分支5.3:优化技巧

七、复习清单

✅ 本章必背知识点清单

概念理解
  • 能准确区分满二叉树和完全二叉树
  • 理解BST的中序遍历是有序序列
  • 掌握AVL树的平衡因子定义
  • 理解哈夫曼编码的前缀特性
代码实现
  • 能手写BST的插入、查找、删除操作
  • 能手写AVL树的四种旋转
  • 能实现并查集的路径压缩
  • 记住BST平均复杂度O(log n),最坏O(n)
  • 记住AVL树所有操作都是O(log n)
应用能力
  • 会手工构造AVL树并判断旋转类型
  • 能计算哈夫曼树的WPL
  • 会求哈夫曼编码
  • 掌握并查集的应用场景
真题要点
  • 掌握BST删除节点的三种情况
  • 记住AVL旋转的判断:“单旋看孩子,双旋看孙子”
  • 熟练手工构造哈夫曼树

八、知识拓展

前沿应用

  • BST:数据库索引的基础结构
  • AVL树:需要频繁查找的场景,如内存管理
  • 哈夫曼编码:文件压缩(ZIP)、图像压缩(JPEG)
  • 并查集:社交网络好友关系、最小生成树算法

常见误区

  1. ❌ 完全二叉树一定是满二叉树
    ✅ 满二叉树一定是完全二叉树
  2. ❌ BST的删除只需要用前驱替代
    ✅ 可以用前驱或后继替代,通常用后继
  3. ❌ AVL树的平衡因子可以是±2
    ✅ 平衡因子只能是-1、0、1
  4. ❌ 哈夫曼树是唯一的
    ✅ 形态可能不唯一,但WPL唯一

记忆技巧

  • AVL旋转:“左左右旋转,右右左旋转,左右先左后右,右左先右后左”
  • BST删除:“叶子直接删,独子用子替,双子找后继”
  • 并查集:“查找压缩路径短,合并按秩树不高”

自测题

  1. 一棵有n个节点的完全二叉树,其叶子节点数为?
    A. n/2 B. ⌊n/2⌋ C. ⌈n/2⌉ D. n-1

  2. 在AVL树中插入一个节点后,最多需要几次旋转?
    A. 1 B. 2 C. log n D. n

  3. 字符a、b、c、d的频率为1:2:3:4,哈夫曼编码的平均长度为?
    A. 1.8 B. 2.0 C. 2.2 D. 2.4

答案:1.C 2.B 3.B

结语

特殊树结构是408数据结构的核心难点,每种树都有其独特的性质和应用场景。通过本文的学习,你已经掌握了:

  1. 🎯 完全二叉树和满二叉树的判定
  2. 🎯 BST的三种删除情况处理
  3. 🎯 AVL树的四种旋转操作
  4. 🎯 哈夫曼树的构造和编码
  5. 🎯 并查集的优化技巧

这些特殊树结构是理解B树、B+树、红黑树等高级数据结构的基础。下一篇文章,我们将进入图论的世界,探索《图论基础:存储结构与遍历算法》,这同样是408的必考重点。

💪 学习建议:特殊树结构需要大量练习才能熟练掌握。建议每天手工模拟1-2道树的构造题,特别是AVL树的旋转和哈夫曼树的构造。记住,408考试更看重手工计算的准确性!加油!


备注:本文所有代码均符合C99标准,适用于408统考要求。

http://www.dtcms.com/a/340709.html

相关文章:

  • HTTPS协议与HTTP协议的区别
  • Web前端调试与性能优化,Charles抓包工具的高效应用
  • 计算机视觉(二)------OpenCV图像视频操作进阶:从原理到实战
  • vscode连接docker
  • 【网络运维】Linux:正则表达式
  • Gin自定义Error中间件
  • 【C++】--指针与引用深入解析和对比
  • Gin传参和接收参数的方式
  • K8S-Secret资源对象
  • 如何代开VSCode的settigns.json文件
  • 【运维】githubvercel学习使用
  • 数据结构--2:ArrayList与顺序表
  • 【机器学习深度学习】AI大模型高并发挑战:用户负载部署策略
  • 26_基于深度学习的茶叶等级检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  • 【JavaEE】多线程 -- CAS机制(比较并交换)
  • iPhone17系列超全准确预告
  • 【windows】只需两步繁杂的桌面开启清爽模式
  • 大数据常见问题分析与解决方案
  • 对抗式域适应 (Adversarial Domain Adaptation)
  • C++继承中的虚函数机制:从单继承到多继承的深度解析
  • VLN领域的“ImageNet”打造之路:从MP3D数据集、MP3D仿真器到Room-to-Room(R2R)、VLN-CE
  • Linux-文件查找find
  • pyqt 的自动滚动区QScrollArea
  • electron进程间通信-从主进程到渲染器进程
  • 康师傅2025上半年销售收入减少超11亿元,但净利润增长20.5%
  • qwen 千问大模型联网及json格式化输出
  • Https之(一)TLS介绍及握手过程详解
  • 【数据结构】排序算法全解析:概念与接口
  • 从0开始学习Java+AI知识点总结-20.web实战(多表查询)
  • HTTPS 原理