当前位置:网站首页>AVL树插入操作与验证操作的简单实现
AVL树插入操作与验证操作的简单实现
2022-07-06 21:03:00 【芜湖开冲~】
目录
AVL树的概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
就是说,这两个人在二叉树搜索树的基础上面做了一些优化,原来的二叉搜索树可能会退化为单只树,搜索时间复杂度为O(n),现在保证了这个树是基本平衡的,所以搜索的时间复杂度成为了O(logn)
AVL树的特征
一棵AVL树,要么是空树,要么具有这两个特征
它的左右子树都是AVL树
左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 ,搜索时间复杂度O( logn)。
至于这个平衡因子是什么,简单,就是定义树节点的时候多加一个属于平衡因子的变量就行了
这里再贴一副图,大家感受一下
圆圈里面的数字表示节点的值,红色数字表示节点的平衡因子,平衡因子可以用右子树减去左子树,也可以用左子树减去右子树,这里采用的是右子树减去左子树,底下也是一样的
AVL树节点的定义
template <class T>
class AVLTreeNode {
public:
AVLTreeNode<T>* _left;
AVLTreeNode<T>* _right;
AVLTreeNode<T>* _parent;
T _value;
int _bf; //平衡因子,约定平衡因子为右边树的高度减去左边树的高度
//构造函数
AVLTreeNode(const T& value = T())
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_value(value)
,_bf(0)
{}
};
这里的节点我们采用的是双亲表示发,因为后面要用到双亲,再求有点复杂
准备工作
template <class T>
class AVLTree {
typedef AVLTreeNode<T> Node;
public:
AVLTree()
:_root(nullptr)
{}
~AVLTree() {
Distory(_root);
}
private:
void Distory(Node*& root) {
if (root) {
Distory(root->_left);
Distory(root->_right);
delete root;
root = nullptr;
}
}
Node* _root;
};
注意Distory的参数是Node*的引用
AVL树的插入
按照二叉搜索树的方式插入新节点
我们先按照二叉搜索树的插入方式给它插进去,不会二叉搜索树的插入也没关系,这里直接看就行
bool Insert(const T& value) {
//判断要插入的位置
//如果AVL树为空
if (_root == nullptr) {
_root = new Node(value);
return true;
}
//如果AVL树不空
//找节点,先插进去;
Node* parent = _root;
Node* child = parent;
while (child) {
parent = child;
if (value > child->_value) {
child = child->_right;
}
else if (value < child->_value) {
child = child->_left;
}
else {
return false;
}
}
//节点找到了,插入,更新节点里面的指针
child = new Node(value);
child->_parent = parent;
if (child->_value > parent->_value) {
parent->_right = child;
}
else {
parent->_left = child;
}
注意,上面这个函数没完,只是因为太长了,所以放为两部分
调整节点的平衡因子(*****)
五星,重中之重
先来说一下调整平衡因子的四种方法,左单旋,右单旋,右左双旋,左右双旋
但其实我们只用看一种,其他的几种就很简单了
我们来看左单旋
看这幅图,现在是平衡的,但是如果我们再加一个70,它就不平衡了
这时,可以看到30这个节点的平衡因子是2,不符合AVL树的特性,所以我们应该对这一部分进行调整,对于这种场景(新节点加到较高右子树的右测节点),我们应该左单旋,所谓左单旋,我们给平衡因子不符合的节点命名为parent,给它的右孩子起名叫subR,然后把parent设置为subR的左孩子,subR原本的左孩子(如果有的话)设置成为parent新的右孩子,如果parent有双亲的话,让parent的双亲指向subR节点就好,调整以后的图片如图
平衡因字我们暂时没动,从图片可以看出,原本的parent节点和subR节点的平衡因子不对,应该改为0,所以后面再把这两个节点的平衡因子改为0即可
右单旋(适用情况:在较高左子树的左侧插入节点)和左单旋差不对,就是把parent的左孩子节点上提而已,没什么好说的
接下来我们看第三种情况,右左双旋(适用情况:在较高右子树的左边插入节点)
听名字就知道怎么做了,先右单旋(右单旋parent的右孩子)再左单旋么,当然,你问我为什么我怎么就知道这么做?那是前人总结出来的,我试了一下发现是对的,仅此而已,你也可以试一下,要是错的你就赚大发了
两次旋转以后平衡因子可能会不对,那如何修改呢?也简单,你试一下,到最后就会发现,每次只有一个节点的平衡因子是不对的
这是人家发现的规律,当然可能不止这一个
Node* subR = parent->_right;
int bf = subR->_left->_bf;
RotateRight(parent->_right);
RotateLeft(parent);
if (bf == 1) {
parent->_bf = -1;
}
else if (bf == -1) {
subR->_bf = 1;
}
中间两个函数第一个是右单旋,第二个是左单旋,这个我觉得会画就行,或者记住也行,实在没有什么要着重说明的
然后是左右双旋(适用情况:在较高左子树的右侧插入节点)
就是先对parent的左孩子来一次左单旋,再对parent来一次右单旋就可以了
然后也是有一个节点的平衡因子不对,需要你取手动调整
Node* subL = parent->_left;
int bf = subL->_right->_bf;
RotateLeft(parent->_left);
RotateRight(parent);
if (bf == 1) {
subL->_bf = -1;
}
else if (bf == -1) {
parent->_bf = 1;
}
最后,看一下整体的代码,中间还有检测这棵树是否为AVL树以及前序遍历的代码
整体代码
template <class T>
class AVLTree {
typedef AVLTreeNode<T> Node;
public:
AVLTree()
:_root(nullptr)
{}
~AVLTree() {
Distory(_root);
}
bool Insert(const T& value) {
//判断要插入的位置
//如果AVL树为空
if (_root == nullptr) {
_root = new Node(value);
return true;
}
//如果AVL树不空
//找节点,先插进去;
Node* parent = _root;
Node* child = parent;
while (child) {
parent = child;
if (value > child->_value) {
child = child->_right;
}
else if (value < child->_value) {
child = child->_left;
}
else {
return false;
}
}
child = new Node(value);
child->_parent = parent;
if (child->_value > parent->_value) {
parent->_right = child;
}
else {
parent->_left = child;
}
//更新双亲的平衡因子
while (parent) {
//child插入会对平衡因子产生影响
if (child == parent->_left) {
parent->_bf--;
}
else {
parent->_bf++;
}
//判断插入后是否满足AVL树的特性
if (parent->_bf == 0) {
break;
}
else if (parent->_bf == 1 || parent->_bf == -1) {
//parent变为1或者-1,肯定会对parent的双亲也产生影响
child = parent;
parent = parent->_parent;
}
else {
//parent的_bf变为了2或者-2,这时候就需要调整了
if (parent->_bf == 2) {
if (child->_bf == 1) {
RotateLeft(parent);
}
else {
RotateRL(parent);
}
}
else {
if (child->_bf == -1) {
RotateRight(parent);
}
else {
RotateLR(parent);
}
}
break;
}
}
return true;
}
//中序遍历
void InOrder() {
_InOrder(_root);
}
//判断是否为AVL树
bool IsAVLTree() {
return _IsAVLTree(_root);
}
private:
//左单旋
void RotateLeft(Node* parent) {
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
//处理subRL节点
if (subRL) {
subRL->_parent = parent;
}
//处理parent节点
Node* pparent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
subR->_parent = pparent;
//处理parent的双亲
if (pparent) {
if (parent == pparent->_left) {
pparent->_left = subR;
}
else {
pparent->_right = subR;
}
}
else { //旋转之前,parent是根节点
_root = subR;
}
//更新平衡因子
parent->_bf = subR->_bf = 0;
}
//右单旋
void RotateRight(Node* parent) {
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
//处理subLR
if (subLR) {
subLR->_parent = parent;
}
//处理parent节点
Node* pparent = parent->_parent;
parent->_parent = subL;
subL->_right = parent;
subL->_parent = pparent;
//处理pparent
if (pparent) {
if (parent == pparent->_left) {
pparent->_left = subL;
}
else {
pparent->_right = subL;
}
}
else {
//parent是root节点
_root = subL;
}
//更新平衡因子
parent->_bf = subL->_bf = 0;
}
//右左双旋
void RotateRL(Node* parent) {
Node* subR = parent->_right;
int bf = subR->_left->_bf;
RotateRight(parent->_right);
RotateLeft(parent);
if (bf == 1) {
parent->_bf = -1;
}
else if (bf == -1) {
subR->_bf = 1;
}
}
//左右双旋
void RotateLR(Node* parent) {
Node* subL = parent->_left;
int bf = subL->_right->_bf;
RotateLeft(parent->_left);
RotateRight(parent);
if (bf == 1) {
subL->_bf = -1;
}
else if (bf == -1) {
parent->_bf = 1;
}
}
//中序遍历
void _InOrder(Node* root) {
if (root == nullptr) {
return;
}
_InOrder(root->_left);
cout << root->_value << " ";
_InOrder(root->_right);
}
//获取树的高度
int GetHeight(Node* root) {
if (root == nullptr) {
return 0;
}
return GetHeight(root->_left) > GetHeight(root->_right) ? GetHeight(root->_left) + 1 : GetHeight(root->_right) + 1;
}
//判断是否为AVL树
bool _IsAVLTree(Node* root) {
if (root == nullptr) {
return true;
}
int high = GetHeight(root->_right) - GetHeight(root->_left);
if (high != root->_bf || abs(high) > 1) {
return false;
}
return _IsAVLTree(root->_left) && _IsAVLTree(root->_right);
}
void Distory(Node*& root) {
if (root) {
Distory(root->_left);
Distory(root->_right);
delete root;
root = nullptr;
}
}
Node* _root;
};
要是有什么不懂的,欢迎在评论区留言,大家都是在路上的人,一起努力吧
边栏推荐
- 2022夏每日一题(一)
- 机器学习笔记 - 使用机器学习进行鸟类物种分类
- Not All Points Are Equal Learning Highly Efficient Point-based Detectors for 3D LiDAR Point
- 19.(arcgis api for js篇)arcgis api for js线采集(SketchViewModel)
- Docker部署Mysql8的实现步骤
- CVPR 2022 best paper candidate | pip: six inertial sensors realize whole body dynamic capture and force estimation
- VHDL实现任意大小矩阵乘法运算
- VHDL implementation of arbitrary size matrix multiplication
- Set static IP for raspberry pie
- Enumeration general interface & enumeration usage specification
猜你喜欢
[leetcode] 700 and 701 (search and insert of binary search tree)
Depth analysis of compilation constants, classloader classes, and system class loaders
枚举通用接口&枚举使用规范
web服务性能监控方案
[security attack and Defense] how much do you know about serialization and deserialization?
力扣------路径总和 III
Gpt-3 is a peer review online when it has been submitted for its own research
QT opens a file and uses QFileDialog to obtain the file name, content, etc
1200.Minimum Absolute Difference
Sub pixel corner detection opencv cornersubpix
随机推荐
qt-线程等01概念
一些常用软件相关
24. (ArcGIS API for JS) ArcGIS API for JS point modification point editing (sketchviewmodel)
What is the experience of maintaining Wanxing open source vector database
线性表的查找
注意力机制原理
Delete data in SQL
API data interface of A-share index component data
Clock in during winter vacation
本机mysql
What about SSL certificate errors? Solutions to common SSL certificate errors in browsers
Under the tide of "going from virtual to real", Baidu AI Cloud is born from real
20.(arcgis api for js篇)arcgis api for js面采集(SketchViewModel)
图形化工具打包YOLOv5,生成可执行文件EXE
数据的存储
Index of MySQL
概率论公式
On file uploading of network security
大白话高并发(二)
About Confidence Intervals