目录

搭建“创世”的舞台

注入序列,观察演化

注入 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.pswp.cn/pingmian/93849.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/93849.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/93849.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

github 上传代码步骤

登录GitHub → 点击右上角 ​​ → New Repository​​。填写仓库名称&#xff08;建议与本地项目同名&#xff09;&#xff0c;选择 ​​Public/Private​​。​​关键&#xff1a;不要勾选​​ “Initialize with README”&#xff08;避免与本地仓库冲突&#xff09;。点击 …

陪诊小程序系统开发:开启智慧就医新时代

在数字化浪潮的推动下&#xff0c;智慧医疗正逐渐成为现实。陪诊小程序系统的开发&#xff0c;作为智慧医疗领域的一次重要创新&#xff0c;正以其独特的魅力与优势&#xff0c;引领着就医新时代的到来。它不仅改变了传统就医模式&#xff0c;更以科技的力量&#xff0c;让医疗…

朝花夕拾(七)--------从混淆矩阵到分类报告全面解析​

目录 ​​机器学习模型评估指南&#xff1a;从混淆矩阵到分类报告全面解析​​ ​​1. 引言​​ ​​2. 混淆矩阵&#xff1a;模型评估的基石​​ ​​2.1 什么是混淆矩阵&#xff1f;​​ 2.2二分类问题的混淆矩阵 ​​二分类场景下的具体案例​ ​分析案例: 1.​​案例…

Python读取和设置PNG图片的像素值

在Python中&#xff0c;可以使用Pillow库或OpenCV库来读取和写入PNG图片的像素值。以下是两种方法的详细说明&#xff1a;1. 使用Pillow库Pillow是Python中常用的图像处理库&#xff0c;支持多种图像格式&#xff0c;包括PNG。读取像素值from PIL import Imageimg Image.open(…

SkyWalking + Elasticsearch8 容器化部署指南:国内镜像加速与生产级调优

SkyWalking Elasticsearch8 Docker 部署文档本文提供在 Ubuntu 服务器上&#xff0c;使用 Docker Compose 部署 SkyWalking&#xff08;OAPUI&#xff09;与 Elasticsearch 8 的完整步骤&#xff0c;数据/日志落地到 /media/disk2 前置条件 Ubuntu&#xff0c;已具备 sudo 权限…

有符号和无符号的区别

有符号&#xff08;Signed&#xff09;和无符号&#xff08;Unsigned&#xff09;是计算机编程中用来描述整数数据类型能否表示负数的两个概念。它们的主要区别在于能否表示负数以及数值的表示范围。以下是它们的核心区别&#xff1a;1. 能否表示负数有符号&#xff08;Signed&…

8月21日作业

1、Makefile中头文件发生过修改的解决&#xff1a; 处插入*.h依赖&#xff0c;对.h文件打的时间戳进行检查2、头删和输出//五、头删 void delete_head(seq_p s) {empty(s);for(int i1;i<s->len;i){s->data[i-1]s->data[i];}s->len--; }//六、输出 void output(s…

Lucene 8.5.0 的 `.pos` 文件**逻辑结构**

Lucene 8.5.0 的 .pos 文件**逻辑结构**&#xff08;按真实实现重新整理&#xff09; .pos 文件 ├─ Header (CodecHeader) ├─ TermPositions TermCount ← 每个 term 一段&#xff0c;顺序由词典隐式决定 │ ├─ PackedPosDeltaBlock N ← 仅当 **无 payl…

基于Matlab多技术融合的红外图像增强方法研究

红外图像在低照度、强干扰和复杂环境下具有较强的成像能力&#xff0c;但受传感器噪声、成像条件及大气衰减等因素影响&#xff0c;原始红外图像往往存在对比度低、细节模糊及光照不均等问题。本文针对红外图像质量退化的特点&#xff0c;提出了一种基于多算法融合的红外图像增…

【时时三省】集成测试 简介

山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 目录 1,集成测试含义 2,集成测试 验证方法 3,集成测试 用例设计方法 4,集成测试输出物 5,集成测试注意点 1,集成测试含义 单元测试在以V模型的流程中,对应的是架构设计阶段。在 单元测试 和 架构设计…

leetcode 76 最小覆盖子串

一、题目描述二、解题思路整体思路&#xff1a;模拟寻找最小覆盖子集的过程&#xff0c;由于可借助同向双指针且可以做到指针不回退&#xff0c;所以可以用滑动窗口的思想来解决这个问题。具体思路&#xff1a;(1)数组hash1用于统计t中每一个字符出现的频次&#xff0c;变量kin…

阿里云ECS服务器的公网IP地址

文章目录环境背景查询公网IP地址阿里云控制台阿里云客户端工具&#xff08;图形界面&#xff09;阿里云CLI工具&#xff08;命令行&#xff09;其它方法元数据服务器ipinfo.io参考注&#xff1a;本文介绍了如何获取阿里云ECS服务器的公网IP地址&#xff0c;可以顺便了解一下和阿…

IPSec 与 IKE 核心知识点总结

一、IPSec 安全基础IPSec 是保障 IP 数据传输安全的核心协议&#xff0c;其核心围绕密钥管理和安全策略约定展开&#xff0c;具体包括以下关键内容&#xff1a;1. 对称密钥的作用与要求对称密钥是 IPSec 实现加密、验证的基础&#xff0c;主要用于三个场景&#xff1a;加密 / 解…

C2ComponentStore

1. C2ComponentStore这是 Codec 2.0 HAL 的抽象接口&#xff08;frameworks/av/media/codec2/core/include/C2ComponentStore.h&#xff09;。代表一个「组件工厂」&#xff0c;负责&#xff1a;枚举当前可用的 Codec2 组件&#xff08;解码器、编码器&#xff09;。创建组件&a…

AI 在医疗领域的应用与挑战

引言介绍 AI 技术迅猛发展的大背景&#xff0c;引出其在医疗领域的重要应用。阐述研究 AI 医疗应用及挑战对推动医疗行业进步的重要意义。AI 在医疗领域的应用现状疾病诊断辅助&#xff1a;描述 AI 影像识别技术在识别 X 光、CT、MRI 影像中疾病特征的应用&#xff0c;如对肺癌…

【GPT入门】第51课 Conda环境迁移教程:将xxzh环境从默认路径迁移到指定目录

【GPT入门】第51课 Conda环境迁移教程&#xff1a;将xxzh环境从默认路径迁移到指定目录步骤1&#xff1a;创建目标目录&#xff08;若不存在&#xff09;步骤2&#xff1a;克隆环境到新路径步骤3&#xff1a;验证新环境可用性步骤4&#xff1a;删除旧环境&#xff08;可选&…

应急响应-模拟服务器挂马后的应急相关操作

工具&#xff1a;攻击机&#xff1a; kail:192.168.108.131 kail下载地址&#xff1a;https://mirrors.aliyun.com/kali-images/kali-2021.3/kali-linux-2021.3-live-i386.iso靶机&#xff1a;windows 7: 192.168.108.1321、在kali中制作木马文件&#xff1a;vhost.exe&#xf…

记一次 .NET 某光谱检测软件 内存暴涨分析

一&#xff1a;背景 1. 讲故事 训练营里的一位学员找到我&#xff0c;说他们的系统会出现内存暴涨的情况&#xff0c;看了下也不是托管堆的问题&#xff0c;让我协助一下到底怎么回事&#xff1f;既然有dump了&#xff0c;那就开始分析之旅吧。 二&#xff1a;内存暴涨分析 1. …

基于OpenCV的物体识别与计数

在计算机视觉领域&#xff0c;利用图像处理技术进行物体识别和计数是一项基础且重要的任务。本文将介绍一种使用OpenCV库实现的高效物体识别与计数方法&#xff0c;并提供一些代码片段以帮助理解各个步骤。 这是前几年做过传统图像处理计数的项目&#xff0c;通过传统图像处理之…

算法题打卡力扣第34题:在排序数组中查找元素的第一个和最后一个位置(mid)

题目描述提示&#xff1a; 0 < nums.length < 105 -109 < nums[i] < 109 nums 是一个非递减数组 -109 < target < 109 解题思路一 暴力解 头到尾遍历整个数组。 用一个变量 first 记录第一次遇到 target 的索引。 继续遍历&#xff0c;用另一个变量 last 不断…