平衡二叉树(AVLTree)

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.mzph.cn/diannao/2325.shtml

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

相关文章

(超级详细)JAVA之Stream流分析-------持续更新喔!!!

学习目标&#xff1a; 掌握 Java Stream流的相关api 掌握 Java Stream流的基本实现 掌握 java Stream流的使用场景 代码已经整理上传到了gitee中&#xff0c;有需要的小伙伴可以取查看一下源码点个小心心喔 大家也可以帮我提交一点案例喔&#xff01;&#xff01;&#xff01;&…

Springboot+vue的高校毕业与学位资格审核系统。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的高校毕业与学位资格审核系统。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#x…

SpringMVC interceptor有时候配置的时候path=“/**“ 两个星号什么意思,与path=“/“以及path=“/*“什么区别

直接上案例&#xff1a; <mvc:interceptor> <mvc:mapping path"/**"/> <bean class"com.xuyang.interceptor.user.UserAuthInterceptor" /> </mvc:interceptor>/**的意思是所有文件夹及里面的子文件夹 /*是所有文件夹&#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混合编程之使…

统一SQL 支持Oracle number/decimal/dec/numeric转换

统一SQL介绍 https://www.light-pg.com/docs/LTSQL/current/index.html 源和目标 源数据库&#xff1a;Oracle 目标数据库&#xff1a;Postgresql&#xff0c;TDSQL-MySQL&#xff0c;达梦8&#xff0c;LightDB-Oracle 操作目标 通过统一SQL&#xff0c;将Oracle中的numb…

【MATLAB源码-第196期】基于matlab的A*融合DWA算法栅格路径规划仿真,画出路径图、姿态角度以及线角速度。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 A算法与DWA算法的融合是一个高效的路径规划策略&#xff0c;这种策略将A算法的全局路径规划能力与DWA算法的局部避障能力结合起来&#xff0c;以期达到更快、更安全的导航效果。以下是对这种融合策略的详细描述。 一、基本概…

Java Future模式

前言 Future模式是并发编程的一个重要的设计模式。比如有个方法需要很长的时间才能得到结果&#xff0c;不会让调用的程序一直等待&#xff0c;而是先返回给它一张“提货卡”。其实相当于消息队列&#xff0c;当你下了订单之后&#xff0c;在并发情况下&#xff0c;实际不是即时…

Linux thermal框架介绍

RK3568温控 cat /sys/class/thermal/thermal_zone0/temp cat /sys/class/thermal/thermal_zone1/temp cat /sys/class/thermal/cooling_device0/cur_state cat /sys/class/thermal/cooling_device1/cur_state cat /sys/class/thermal/cooling_device2/cur_state thermal_zone…

【LeetCode热题100】【链表】排序链表

题目链接&#xff1a;148. 排序链表 - 力扣&#xff08;LeetCode&#xff09; 要排序一个链表&#xff0c;最快的方法是用一个数组将链表节点的值存起来然后排序数组后重新构建链表 但是从面试的角度&#xff0c;我们应该在链表原地排序&#xff0c;这里使用最简单的归并排序…

信息打点--公众号服务

微信公众号 获取微信公众号的途径https://weixin.sogou.com/ 微信公众号没有第三方服务 Github监控 人员&域名&邮箱 eg&#xff1a;xxx.cn password in:file https://gitee.com/ https://github.com/ https://www.huzhan.com/ 资源搜索 in:name test 仓库标题搜索含有…

ASP.NET教务管理平台-权限及公共模块设计与开发

摘 要 随着教育改革的不断深化&#xff0c;高等院校的建设与发展对国民整体素质的提高起着越来越重要的作用&#xff0c;建立一套能够适应这些改变的行政管理方案也就显得尤为重要。对于教务处来说&#xff0c;将信息技术用于校务管理中便是迫切的要求。 教务系统中的用户…

产品规划|如何从0到1规划设计一款产品?

我们要如何从0到1规划设计一款产品?在前期工作我们需要做什么呢?下面这篇文章就是关于此的相关内容,大家一起往下看多多了解了解吧! 一、什么是产品规划? 产品规划是一种策略,它设定了产品的价值和目标,并确定实施方案以实现这些目标。它考虑了产品的整个生命周期,基于…

HCIP-Datacom-ARST必选题库_42_排错【1道题】

一、简单题 1.在遇到网络故障时&#xff0c;工程师经常使用分层故障处理法。因为所有模型都遵循相同的基本前提&#xff0c;当模型的所有低层结构工作正常时&#xff0c;它的高层结构才能正常工作。请根据分层法将下列检查项和对应的层次匹配。TCP连接是否正确建立&#xff0c…

22长安杯电子取证复现(检材一,二)

检材一 先用VC容器挂载&#xff0c;拿到完整的检材 从检材一入手&#xff0c;火眼创建案件&#xff0c;打开检材一 1.检材1的SHA256值为 计算SHA256值&#xff0c;直接用火眼计算哈希计算 9E48BB2CAE5C1D93BAF572E3646D2ECD26080B70413DC7DC4131F88289F49E34 2.分析检材1&am…

dremio支持设置

Dremio 支持提供可用于诊断目的的设置。这些设置通过 Dremio UI&#xff1a;设置>支持启用&#xff08;或禁用&#xff09; 使用 Client Tools 可以配置当用户查看数据集中的数据时&#xff0c;Dremio 项目的工具栏上显示哪些客户端应用程序按钮。用户可以通过单击相应的工具…

海外媒体广告投放 - 大舍传媒助力企业迈向新台阶,实现精准投放

一、为何选择海外媒体广告投放 随着全球化进程的不断推进&#xff0c;越来越多的企业开始将目光投向国际市场。海外媒体广告投放作为一种有效的宣传手段&#xff0c;可以帮助企业在全球范围内提高品牌知名度和影响力&#xff0c;吸引潜在客户&#xff0c;促进产品销售。 二、…

XiaodiSec day019 Learn Note 小迪安全学习笔记

XiaodiSec day019 Learn Note 小迪安全学习笔记 记录得比较凌乱&#xff0c;不尽详细 C#相关 .NET 框架&#xff0c;多用 C#开发 内容非常少&#xff0c;和通用安全漏洞差不多 未授权访问 目录结构 反编译获得源码&#xff0c;dll 反编译 web.config 目录 dll 文件类似于…

12、【装饰器模式】动态地为对象添加新功能

你好&#xff0c;我是程序员雪球。 今天我们来聊聊 23 种设计模式中&#xff0c;一种常见的结构型模式&#xff0c;装饰器模式。聊聊它的设计思想、实现原理&#xff0c;应用场景&#xff0c;以及如何使用。 装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型…

使用d3.js画一个BoxPlot

Box Plot 在画Box Plot之前&#xff0c;先来了解下Box Plot是什么&#xff1f; 箱线图&#xff08;Box Plot&#xff09;也称盒须图、盒式图或箱型图&#xff0c;是一种用于展示数据分布特征的统计图表。 它由以下几个部分组成&#xff1a; 箱子&#xff1a;表示数据的四分…

ruoyi element-ui 实现拖拉调整图片顺序

ruoyi element-ui 实现拖拉调整图片顺序 安装sortablejs https://sortablejs.com/npm 安装sortablejs npm install sortablejs --save相关options var sortable new Sortable(el, {group: "name", // or { name: "...", pull: [true, false, clone, …