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

数据结构:生成 (Generating) 一棵 AVL 树

目录

搭建“创世”的舞台

注入序列,观察演化

注入 10

注入 20

注入 30

注入 40

注入 50

注入 25

再次审视


上一讲,我们已经从最根本的逻辑出发,推导出了 AVL 树失衡时所必需的修复操作——旋转 (Rotation)。

现在,我们将进入一个非常实践性的环节:如何从无到有地生成 (Generating) 一棵 AVL 树?

这个问题的本质,其实是一个思想实验:

如果我们拥有一台“AVL 插入机”(也就是我们上一讲实现的 insert 函数),我们把一堆数字一个一个地扔给它,这台机器内部会发生什么?最终会得到一个什么样的产品?

搭建“创世”的舞台

在开始之前,我们需要三样东西:

1. 一个起点 (The Starting Point): 一个空的世界,也就是一棵空树。在 C/C++ 代码中,我们用一个 NULL 指针来表示。

// 我们的宇宙之初,只有一个指向虚空的根指针
AVLTreeNode* root = NULL;

2. 一套基本法则 (The Rules): 这就是我们上一讲呕心沥血推导出的 insert 函数。它内部封装了 BST 的插入逻辑、高度 (Height) 的更新、平衡因子 (Balance Factor, BF) 的检查,以及四种旋转 (Rotation) 修复机制。

3. 一串生命序列 (The Sequence): 我们将要注入的数字。为了充分展示树的演化过程,我们需要一个能触发各种情况的序列。我们选用这个经典的序列:[10, 20, 30, 40, 50, 25]

我们的整个“生成”过程,在代码层面其实非常简洁,就是一个循环,不断地调用我们的基本法则 insert 函数。

数据结构:利用旋转在AVL树中维持平衡(Inserting in AVL with Rotation)-CSDN博客

// 引入头文件,以便我们可以打印输出观察每一步
#include <iostream>// ... 此处省略我们之前已经定义的 AVLTreeNode 结构体、
// createNode, getHeight, max, leftRotate, rightRotate, insert 等函数 ...// 主程序,也就是我们的“创世”过程
int main() {AVLTreeNode* root = NULL; // 1. 起点:空树int keys[] = {10, 20, 30, 40, 50, 25}; // 3. 生命序列int n = sizeof(keys) / sizeof(keys[0]);std::cout << "Starting to generate the AVL tree..." << std::endl;std::cout << "------------------------------------" << std::endl;for (int i = 0; i < n; ++i) {std::cout << "\n>>> Inserting " << keys[i] << "..." << std::endl;// 2. 反复调用我们的基本法则root = insert(root, keys[i]);// 为了便于观察,我们可以在这里加上一个打印树结构的函数(此处省略其实现)// printTree(root); std::cout << ">>> ... " << keys[i] << " inserted. Tree is now balanced." << std::endl;}std::cout << "\n------------------------------------" << std::endl;std::cout << "Generation complete." << std::endl;return 0;
}

代码框架已经搭好,现在,让我们进入核心环节,手动模拟上面这段代码的执行过程,观察树的每一步演化。


注入序列,观察演化

注入 10
  • 演化过程: 树为空,insert 函数直接创建一个新节点作为根。

  • 最终形态:树诞生了,它是平衡的。

(10) {h=0, BF=0}  // h 是 Height(高度), BF 是 Balance Factor(平衡因子)
注入 20
  • 演化过程:

    1. 根据 BST 法则, 20 > 10,插入到 10 的右侧。

    2. 插入后回溯,更新路径上的节点。节点 10 的高度 h 更新为 1,其平衡因子 BF 变为 height(left) - height(right) = -1 - 0 = -1

    3. |BF| <= 1,树依然平衡。

  • 最终形态:

  (10) {h=1, BF=-1}\(20) {h=0, BF=0}
注入 30
  • 演化过程:

    1. 根据 BST 法则, 30 > 10 (向右) -> 30 > 20 (向右),插入到 20 的右侧。

    2. 回溯更新:

      • 20 节点:h 更新为 1, BF 更新为 -1。平衡。

      • 10 节点:h 更新为 2, BF 更新为 height(left) - height(right) = -1 - 1 = -2

    3. 警报! 节点 10BF 绝对值变为 2树失衡 (Unbalanced)!

  • 诊断与修复:

    • 失衡节点 A10 (BF=-2)。

    • 失衡是由于在 A 的右 (Right)子树 (B=20) 的右 (Right)侧插入新节点导致的。

    • 这是典型的 RR (右-右) 失衡。

    • 根据我们的法则,需要对失衡节点 10 执行一次 左旋 (Left Rotation)。

  • 修复后形态:

  (20) {h=1, BF=0}/  \
(10) (30) {h=0, BF=0}
注入 40
  • 演化过程: 40 根据 BST 法则插入为 30 的右孩子。回溯检查,所有节点的 BF 均在 [-1, 1] 区间内。

  • 最终形态: 无需旋转。

  (20) {h=2, BF=-1}/  \
(10) (30) {h=1, BF=-1}\(40) {h=0, BF=0}
注入 50
  • 演化过程:

    1. 50 插入为 40 的右孩子。

    2. 回溯更新,当检查到 30 节点时,其 h 变为 2,BF 变为 -2失衡!

  • 诊断与修复:

    • 失衡节点 A30 (BF=-2)。

    • 失衡路径是 右-右 (RR)。

    • 修复操作:对 30 执行 左旋 (Left Rotation)。40 会取代 30 的位置,成为 20 的右孩子。

  • 修复后形态:

      (20) {h=2, BF=-1}/  \(10) (40) {h=1, BF=0}/  \(30) (50)

系统再次自我修复,恢复平衡。

注入 25
  • 演化过程:

    1. 根据 BST 法则: 25>20 (右) -> 25<40 (左) -> 25<30 (左)。25 插入为 30 的左孩子。

    2. 回溯更新:

      • 30 节点: h 变为 1, BF 变为 1。平衡。

      • 40 节点: h 变为 2, BF 变为 1。平衡。

      • 20 节点: h 变为 3, BF 变为 height(left) - height(right) = 0 - 2 = -2。 失衡!

  • 诊断与修复:

    • 失衡节点 A20 (BF=-2)。

    • 失衡发生在 A右 (Right)子树 (根为 40)。

    • 新节点 25 是插入在该子树的左 (Left)侧

    • 这是一个 RL (右-左) 的“拐角”型失衡。

    • 根据法则,需要两步操作:

      1. “拉直”拐角: 对 A 的孩子节点 40,执行一次右旋 (Right Rotation)。

      2. 整体修复: 对 A 节点 20,执行一次左旋 (Left Rotation)。

  • 修复后最终形态:

      (30)/    \(20)    (40)/  \      \
(10) (25)    (50)

经历了一次复杂的重构后,系统演化出了一个极为稳定和平衡的最终形态。


再次审视

我们完成了整个生成过程。回过头看,我们得到了什么结论?

  • 自组织性 (Self-Organization): 我们从未规划过最终那棵树长什么样。我们只定义了最基本的、局部的交互规则(插入和旋转)。一个高度平衡的、优美的全局结构,是自发涌现出来的。

  • 动态平衡 (Dynamic Equilibrium): AVL 树不是静止的。每次有新的元素加入,都会暂时打破它的平衡,然后系统会通过一系列的调整,迅速回归到一个新的平衡状态。这是一种动态的、有弹性的稳定。

  • 局部决定全局 (Local Actions, Global Consequences): 每次修复操作(旋转)都只涉及两到三个节点。然而,就是这样微小的局部调整,确保了整棵树的全局属性——对数级别的高度,从而保证了 O(logn) 的操作效率。

这就是从第一性原理理解“生成 AVL 树”——它不是一个按图索骥的建造过程,而是一个遵循简单规则、不断演化的创生过程。

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

相关文章:

  • 域名污染怎么清洗?域名污染如何处理?
  • 8.21作业
  • 【运维进阶】if 条件语句的知识与实践
  • AI设计师-标小智旗下AI在线设计平台
  • 洛谷 P4942 小凯的数字-普及-
  • Hybrid laser 是什么?
  • BFS算法C++实现(邻接表存储)
  • 最爱--中岛美雪
  • 8 月 20 日科技新动态:多领域创新成果涌现
  • 【typenum】 19 类型相同检查(type_operators.rs片段)
  • Esp32基础(⑩超声波测距模块)
  • Pycharm SSH连接
  • Wireshark数据包波形绘制异常
  • [RestGPT] docs | RestBench评估 | 配置与环境
  • 【51单片机】【protues仿真】基于51单片机16键电子琴系统
  • 【GPT入门】第51课 Conda环境迁移教程:将xxzh环境从默认路径迁移到指定目录
  • OpenAI 开源模型 gpt-oss 是在合成数据上训练的吗?一些合理推测
  • Mysql事务特性
  • python实现根据接口返回数据生成报告和图表
  • (第二十期下)超链接的更多分类
  • 医疗元宇宙:破解医疗困局与数字化变革路径
  • gRPC 服务发现选型对比
  • 基于STM32单片机的二维码识别物联网OneNet云仓库系统
  • 最小生成树的普利姆算法和克鲁斯卡尔算法
  • ABP vNext 速率限制在多租户场景落地
  • Leetcode 深度优先搜索 (13)
  • Leetcode 深度优先搜索 (12)
  • 20250821 圆方树总结
  • 通信基础理论
  • C语言基础习题——01