平衡二叉树(AVLTree)

news/2024/5/6 17:31:03

AVLTree

  • 1、树的分类
  • 2、平衡二叉树
    • 2.1、构建一个平衡二叉树
    • 2.2、删除节点
    • 2.3、搜索方式
      • 2.3.1、广度优先搜索(BFS)
      • 2.3.2、深度优先搜索(DFS)

1、树的分类

树形结构是编程当中特别常见的一种数据结构。比如电脑中的文件管理系统就大量用到树结构。
![在这里插入图片描述](https://img-
##blog.csdnimg.cn/direct/2f412f2f7b144da3b4193557dc388274.png#pic_center =600x400)
该图取之于这篇文章 硬核总结!真二叉树、满二叉树、完全二叉树的性质与概念,这篇文章对一些基础的树总结的很好。所以我也不赘诉了。

2、平衡二叉树

平衡二叉树的基础是二叉搜索树,对于一个二叉搜索树而言,新插入的数字若小于根节点则放在左边,反之放在右边。但如果插入的数字一直小于或大于根节点,那这个树,就和链表差不多了。如图,我按顺序插入(1, 0, 3, 2, 4, 5, 6)。
在这里插入图片描述
本来二叉树的实现就是为了减低搜索的时间复杂度的,而如果树构建得和单链表差不多,那便失去了意义。所以二叉平衡搜索树,又称平衡二叉树应运而生。
平衡二叉树要求左子树的高度和右子树的高度之不能超过1。

2.1、构建一个平衡二叉树

正如前面所言,一个平衡二叉树,首先得是一个二叉搜索树,然后再要求左右子树高度差不超一。于是人们总结出四种会导致不平衡的情况。
1、LL型:即左子树的左子树增高了一层,从而导致不平衡。
在这里插入图片描述
上图是一个简单的模型,实际上为了解决不平衡的问题,要解决的事情远不止怎么简单。下面是一个更为通用的模型。我不仅需要把a变成b的右子树,还需要把原来b的右子树变成a的左子树,然后还要让原来指向a的指针指向b。这是一个颇为麻烦的事情,最难的部分是要改变根节点上级指针。
在这里插入图片描述
2、LR型:左子树的右子树添加了一层导致不平衡。直接上图。
在这里插入图片描述
另外两种类型就是RR和RL了,逻辑和上述两种都是一样的。再写代码的时候甚至可以把所以left和right调换然后就可以实现LL到RR的转换或LR到RL的转换。而我懒得画图就不再画了。
直接上代码。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define n 9struct Node
{int data;Node* right;Node* left;Node() {};Node(int x): data(x) {};            //constructor
};class AVLTree
{
public:Node* root;AVLTree(){root = nullptr;};int get_height(Node* node);void append_data(Node** node, int num);       //append a number into the AVL treevoid rrRotation(Node** root);void rlRotation(Node** root);void llRotation(Node** root);void lrRotation(Node** root);
};int AVLTree::get_height(Node* node)
{if(node == nullptr) return 0;return max(get_height(node->left), get_height(node->right)) + 1;
}void AVLTree::append_data(Node** node, int data)
{if(*node == nullptr){*node = new Node(data);(*node)->right = nullptr;(*node)->left = nullptr;}else if(data < (*node)->data){append_data(&(*node)->left, data);if(abs(get_height((*node)->right) - get_height((*node)->left)) > 1){if(data < (*node)->left->data) llRotation(node);else lrRotation(node);}}else{append_data(&(*node)->right, data);if(abs(get_height((*node)->right) - get_height((*node)->left)) > 1){if(data > (*node)->right->data) rrRotation(node);else rlRotation(node);}}
}void AVLTree::llRotation(Node** root)
{Node* temp = (*root)->left;(*root)->left = temp->right;temp->right = *root;*root = temp;
}void AVLTree::lrRotation(Node** root)
{Node* temp = (*root)->left->right;(*root)->left->right = temp->left;temp->right = *root;Node* temp2 = temp->left;temp->left = (*root)->left;(*root)->left = temp2;*root = temp;
}void AVLTree::rrRotation(Node** root)
{Node* temp = (*root)->right;(*root)->right = temp->left;temp->left = *root;*root = temp;
}void AVLTree::rlRotation(Node** root)
{Node* temp = (*root)->right->left;(*root)->right->left = temp->right;temp->left = *root;Node* temp2 = temp->right;temp->right = (*root)->right;(*root)->right = temp2;*root = temp;
}int randomArray[n] = {16, 3, 7, 11, 9, 26, 18, 14, 15};int main()
{class AVLTree mytree;for(int i = 0; i < n; i++)mytree.append_data(&mytree.root, randomArray[i]);printf("the height of tree is: %d\n", mytree.get_height(mytree.root));system("pause");return 0;
}

在这个代码实现中,最令我头疼的是二级指针这里。我一开始不太明白用二级指针的意义,然后尝试了许多次才不得不使用它。
因为在我插入一个节点之后,我是通过反向遍历的方式去查找哪个节点失衡了。但是此时有一个问题,当我得知这个节点失衡之后,我并不清楚它的上一个节点再哪里,因此我无法改变上一个节点所存储的指针。有点绕,我画个图。
如下面两个图所示,如果b这个节点失衡了,但又因为我用的是反向遍历,所以我并不知道a这个节点的地址。故而我无法改变a->right的指向。而为了解决这个问题,我不得不引入二级指针,让我得到b的地址的同时,也得知a.right的地址。
在这里插入图片描述
在这里插入图片描述
具体逻辑是任何对b的操作,都是先取得&a.right再解引用。这样相当于知道b便知道a.right的地址。
在这里插入图片描述
以上,如果不动手实践的话,大概是看不明白的。。。

2.2、删除节点

下面这个图表红的三个框,分别对应着删除一个节点的三种情况。
情况一:节点位于末端,直接删除即可;
情况二:节点有一个孩子,那么删除之后需要重新建立起父子连接;
情况三:节点有两个孩子,此时有两种策略。第一种策略是把右孩子,即右子树的最小值复制到自己身上。然后把被复制的那个节点删除。第二种策略是把左子树的最大值复制到自己身上,然后把被复制的那个节点删除。
在这里插入图片描述
下面这个图是遇到情况三之后采取策略一的示意图。看到这里,可能许多人还存在一个疑惑,难道不怕2下面还有孩子吗?答案是否定的,因为右子树的最小值,即为最值,自然就是没有孩子的节点了。所以此时删除2,只需要切换到情况一即可。
在这里插入图片描述

2.3、搜索方式

2.3.1、广度优先搜索(BFS)

如图,广度优先搜索是从根节点到末节点,一层一层这些遍历下去的。实现起来也并不复杂,只需要设置一个FIFO容器,比如queue。不断的从容器中读取节点即可。
在这里插入图片描述
queue内部的示意图大概是这样,但要注意,其实这些值不会同时出现在queue当中。
在这里插入图片描述
代码实现如下:

void bfs(Node** root)
{Node* temp = nullptr;queue<Node*> Q;if(*root != nullptr)   Q.push(*root);else return;while(!Q.empty()){temp = Q.front();if(temp->left != nullptr)    Q.push(temp->left);if(temp->right != nullptr)    Q.push(temp->right);cout << temp->data << endl;···Q.pop();}
}

2.3.2、深度优先搜索(DFS)

深度优先搜索分为前序遍历、中序遍历和后序遍历。主要还是依靠递归来实现。在代码上只是改动输出值的位置,相对容易。

void dfs_preorder(Node** node)			//前序
{if(*node == nullptr) return;cout << (*node)->data << endl;dfs_preorder(&((*node)->left));dfs_preorder(&((*node)->right));
}void dfs_inorder(Node** node)			//中序
{if(*node == nullptr) return;dfs_preorder(&((*node)->left));cout << (*node)->data << endl;dfs_preorder(&((*node)->right));
}void dfs_postorder(Node** node)		//后序
{if(*node == nullptr) return;dfs_preorder(&((*node)->left));dfs_preorder(&((*node)->right));cout << (*node)->data << endl;
}

分别看一下前序、中序、后序的打印顺序吧。注意,在以下这些图中,节点中的数字代表的是打印顺序。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


http://www.mrgr.cn/p/17462367

相关文章

Linux加强篇-Vim编辑器

目录 ⛳️推荐 Vim文本编辑器 编写简单文档 配置主机名称 配置网卡信息 配置软件仓库 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 Vim文本编辑器 在Linux系统中一切都…

数据结构基础第5讲

数据结构基础第5讲 树和二叉树 本章内容:考点一:基本术语 1.数的引入2.树的定义 层次,分支,一对多。互不相交的含义:3.结点的分类结点的度:4.结点的关系树的深度:树中结点最大高度称为树的高度(或树的深度)行兄弟结点:在同一层但不是兄弟的结点路径长度:等于路径所通…

C语言(扫雷游戏)

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;关注收藏&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#x…

R语言随机森林RandomForest、逻辑回归Logisitc预测心脏病数据和可视化分析|附代码数据

全文链接:http://tecdat.cn/?p=22596 最近我们被客户要求撰写关于预测心脏病的研究报告,包括一些图形和统计输出。 本报告是对心脏研究的机器学习/数据科学调查分析。更具体地说,我们的目标是在心脏研究的数据集上建立一些预测模型,并建立探索性和建模方法。但什么是心脏研…

数据结构基础第3讲

数据结构基础第3讲 栈及其应用 内容考点一:栈的概念 1.顺序栈的定义:出栈顺序情况计算 给定n个元素,出栈顺序的情形满足卡特兰数,计算公式: \[\frac{C_{2n}^{n}}{n+1} \]例题: 确定第一个出栈的谁。有两种可能: 找带头大哥。栈的顺序存储结构 顺序栈操作顺序栈4要素栈空条…

数据结构基础第4讲

数据结构基础第4讲 队列 内容考点一: 队列概念 代码不考 1.队列的定义考点二:顺序队列的定义考点三顺序队列的性质与操作 4要素:考点四:循环队列的定义 由于顺序队列会存在假溢出问题,引入循环队列。 假溢出:描述:考点五:循环队列的操作判断空满:性质: 考频75%元素个…

深入探索GDB:Linux下强大的调试神器

目录 一、GDB简介&#xff1a;源码级调试的基石 二、GDB基础操作&#xff1a;从入门到熟练 启动与基本命令 三、GDB进阶功能&#xff1a;解锁更深层次的调试能力 1. 回溯追踪&#xff1a;洞察调用栈 2. 动态内存检测&#xff1a;揪出内存问题 3. 条件断点与观察点&#…

【视频】N-Gram、逻辑回归反欺诈模型文本分析招聘网站欺诈可视化|附数据代码

原文链接:https://tecdat.cn/?p=36028 原文出处:拓端数据部落公众号 随着互联网的快速发展,招聘网站已成为求职者与雇主之间的重要桥梁。然而,随之而来的欺诈行为也日益猖獗,给求职者带来了极大的困扰和风险。因此,如何帮助客户有效地识别和防范招聘网站上的欺诈行为,已…

02 IO口的操作

目录前言一、IO的概念1.IO接口2.IO端口二、CPU和外设进行数据传输的方法1.程序控制方式1.1 无条件1.2 查询方式2.中断方式3.DMA方式一、方法介绍和代码编写1.前置知识2.程序方式1.1 无条件方式1.1.1 打开对应的GPIO口1.1.2 初始化对应的GPIO引脚1.1.2.1 推挽输出1.1.2.2 开漏输…

vmstat命令详解

一、参数信息 vmstat 命令是用于报告虚拟内存统计信息的工具&#xff0c;常用于 Unix/Linux 系统上。它可以提供关于系统资源使用情况的详细信息&#xff0c;包括 CPU、内存、虚拟内存、磁盘、系统调用等方面的统计数据。以下是常见的 vmstat 命令参数的详解&#xff1a; vms…

题解 UOJ577【[ULR #1] 打击复读】

别学基本子串结构(这篇没写完)题解 UOJ577【[ULR #1] 打击复读 reference https://www.cnblogs.com/crashed/p/17382894.html https://www.cnblogs.com/sizeof127/articles/17579027.html 字符串——黄建恒,广东实验中学 题目描述 为了提升搜索引擎的关键词匹配度以加大访问…

VUE识别图片文字OCR(tesseract.js)

效果:1&#xff1a; 效果图2&#xff1a; 一、安装tesseract.js npm i tesseract.js 二、静态页面实现 <template><div><div style"marginTop:100px"><input change"handleChage" type"file" id"image-input"…

吴恩达机器学习-第二课-第四周

吴恩达机器学习 学习视频参考b站:吴恩达机器学习 本文是参照视频学习的随手笔记,便于后续回顾。 决策树 决策树模型(Decision Tree Model) 猫分类示例通过决策树模型判断是否为猫 一些术语:根结点,决策节点(包括根结点),叶子结点决策树算法是在所有的决策树模型中选一…

【EI会议征稿】2024年先进机械电子、电气工程与自动化国际学术会议(ICAMEEA 2024)

2024 International Conference on Advanced Mechatronic, Electrical Engineering and Automation ●会议简介 2024年先进机械电子、电气工程与自动化国际学术会议&#xff08;ICAMEEA 2024&#xff09;将汇聚全球机械电子、电气工程与自动化领域的专家学者&#xff0c;共同…

【QT进阶】Qt Web混合编程之使用ECharts显示各类折线图等

往期回顾 【QT进阶】Qt Web混合编程之QWebEngineView基本用法-CSDN博客 【QT进阶】Qt Web混合编程之CMake VS2019编译并使用QCefView&#xff08;图文并茂超详细版本&#xff09;-CSDN博客【QT进阶】Qt Web混合编程之html、 js的简单交互-CSDN博客 【QT进阶】Qt Web混合编程之使…

编译用于Qt的opencv问题解决

CMake was unable to find a build program corresponding to "MinGW Makefiles"解释: 这个错误表明CMake无法找到用于生成Makefiles的构建程序。在使用CMake生成项目文件时,如果指定了"MinGW Makefiles",CMake需要一个Make工具来构建项目,而这个工具通…

破解生产瓶颈,提升时效性——蓝鹏测控推进效率革新

在日益激烈的市场竞争中&#xff0c;蓝鹏公司近日宣布采取一系列措施&#xff0c;旨在解决生产过程中的关键短板问题&#xff0c;特别是设计定稿延迟、原料采购不及时等问题&#xff0c;以确保生产部门能够按时完成订单&#xff0c;提高整体运营效率。 蓝鹏公司位于经济发展活…

操作系统八股

操作系统八股 1. 你了解IO多路复用么? 我们熟悉的 select/poll/epoll 内核提供给用户态的多路复用系统调用,进程可以通过一个系统调用函数从内核中获取多个事件。 select/poll/epoll 是如何获取网络事件的呢?在获取事件时,先把所有连接(文件描述符)传给内核,再由内核返回…

chakra-ui学习笔记(一)

前言:发现chakra-ui也不错,虽然比起antd功能稍少一点。1,Stack与Flex区别 Notes on Stack vs Flex#The Stack component and the Flex component have their children spaced out evenly but the key difference is that the Stack wont span the entire width of the conta…

云原生Kubernetes: K8S 1.29版本 部署Jenkins

目录 一、实验 1.环境 2.K8S 1.29版本 部署Jenkins 服务 3.jenkins安装Kubernetes插件 二、问题 1.创建pod失败 2.journalctl如何查看日志信息 2.容器内如何查询jenkins初始密码 3.jenkins离线安装中文包报错 4.jenkins插件报错 一、实验 1.环境 &#xff08;1&…