高阶数据结构:AVL树

前言:

为什么要有AVL树的概念?AVL树是为了解决什么问题?我们已经知道有搜索二叉树,可以通过比较的方式快速查找元素,但是它有一个致命的弱点------一旦选取靠近边界的元素,那么树的结构的高度就会过高,查找的效率就会降低至log(N),这就失去了其本身的意义,那么有什么方法可以使其的两边的高度平衡呢?AVL树就是为了解决这个问题的其中一种方法。

一. AVL的概念

AVL树实现这里我们引入⼀个平衡因子(balance factor)的概念,每个结点都有⼀个平衡因子,任何结点的平衡因子等于右子树的高度减去左子树的高度,也就是说任何结点的平衡因子等于0/1/-1,AVL树并不是必须要平衡因子,但是有了平衡因子可以更方便我们去进行观察和控制树是否平衡,就像⼀个风向标⼀样。

特性:

AVL树是最先发明的自平衡⼆叉查找树,AVL可以是⼀颗空树,或者具备下列性质的⼆叉搜索树:它的左右子树都是AVL树,且左右子树的高度差的绝对值不超过1。

二.AVL树的模拟实现

1.单节点

特点:

1.使用struct,方便使用成员变量

2.使用pair来包含:用于比较的_key,用于存储的映射值

3.三指针(联系):_parent指向上一个节点,_left指向左节点,_right指向右节点

4.因子:_bf

cpp 复制代码
template<class K,class V>
struct AVLTreeNode
{
	AVLTreeNode(const pair<K,V>&kv)
		:_kv(kv)
		, _parent(nullptr)
		, _left(nullptr)
		, _right(nullptr)
		,_bf(0)
	{}
	pair<K, V>_kv;
	AVLTreeNode<K, V>* _parent;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	int _bf;

};

2.类结构

使用指针_root来管理

3.功能的实现

3.1插入

首先插入操作是基于搜索二叉树的结构,用比较来查找适合插入的位置(这个就不详细讲解了),

然后插入元素可能破坏二叉树的平衡,这时候就需要去调整树的结构。接下来详细讲解步骤。

发现AVL二叉树失去平衡:通过平衡因子的大小,我们是可以了解树是否平衡,但是由于插入新元素,旧的因子就需要更新:

步骤:

1.由插入的节点处开始进行更新因子,刚插入的节点为0,通过节点parent指针走到上一个节点,判断新节点是插入在该节点的左边还是右边,左边就对该节点的平衡因子- -,右边就++,观察平衡因子是否变为2/-2或者变为0,如果都不是就继续往上循环步骤(判断,因子的加减)。

2.发现因子变成0,退出循环。为什么?因为以该0处节点往上看(把该处当作一个左子树或者右子树),新元素的插入1没有影响整体的平衡度,从该节点往下看(把该节点当根),新元素的插入只是将自身变得更加平衡了。

如图

2.发现平衡因子为2/-2需要进行结构调整,但是有以下四种情况

(1)需要进行右旋转

(2)需要进行左旋转

(3)需要进行左右双旋转

(4)需要进行右左双旋转

调整的部分后面再进行详细讲解,我们先来搭一下查找的框架(我们实现的是不允许有相同元素的)

cpp 复制代码
bool insert(const pair<K, V>&kv)
{
	Node* newnode = new Node(kv);
	if (_root == nullptr)
	{
		_root = newnode;
		return true;
	}
	Node* cur = _root;
	Node* parent = nullptr;
	while (cur)
	{
		if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}
	//插入
	if (parent->_kv.first < kv.first)
	{
		parent->_right = newnode;
	}
	else
	{
		parent->_left = newnode;
	}
	newnode->_parent = parent;
	//更新并观察平衡因子
	cur = newnode;
	while (parent)
	{
        //更新平衡因子
		if (parent->_left == cur)
		{
			parent->_bf--;
		}
		else
		{
			parent->_bf++;
		}
        //并不影响整体的情况
		if (parent->_bf == 0)
		{
			break;
		}
        //继续向上遍历
		else if(parent->_bf==1||parent->_bf==-1)
		{
			cur = parent;
			parent = parent->_parent;
		}
        //需要继续调整的情况
		else if (parent->_bf == 2 || parent->_bf == -2)
		{
			//......
            break;
		}
		else
		{
			assert(false);
		}
	}
	return true;

}

3.2结构调整

(1)右旋转

首先介绍一下需要右旋转的情况:

需要右旋转的情况都可以抽象成图中的第一阶段,没有例外,所以我们仅通过左子树头节点的-1就可以判断需要右旋转(-2并不能判断是不是需要右旋转)

对各个节点的指针的指向进行操作就可以了

操作的注意点:

1.subLR可能是空,需要判断(防止链接parent时解引用空指针)

2.当旋转平衡之后,需要更新parent和SubL 的平衡因子,AVL旋转后平衡则SubL的平衡因子为0,且parent的平衡因子也为0'

3.如果parent为_root节点,需要更新_root

cpp 复制代码
void RotateR(Node*parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	parent->_left = subLR;
	if (subLR)//判断是否为空
	{
		subLR->_parent = parent;
	}
	Node* parentparent = parent->_parent;
	subL->_right = parent;
	parent->_parent = subL;
	if (parentparent)//判断是否为空
	{
        //分辨是左子树还是右子树
		if (parentparent->_left == parent)
		{
			parentparent->_left = subL;
		}
		else
		{
			parentparent->_right = subL;
		}
	}
	else//parentparent为空表明parent是_root
	{
		_root = subL;//更新_root
	}
	subL->_parent = parentparent;
	//更新因子
	subL->_bf = parent->_bf = 0;
}

(2)左旋转

需要左旋转的情况都可以抽象成图中的第一阶段,没有例外,所以我们仅通过右子树头节点的1就可以判断需要左旋转(2并不能判断是不是需要左旋转)

具体操作和右旋转完全类似

cpp 复制代码
void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	parent->_right = subRL;
	if (subRL)
	{
		subRL ->_parent = parent;
	}
	Node* parentparent = parent->_parent;
	subR->_left = parent;
	parent->_parent = subR;
	if (parentparent)
	{
		if (parentparent->_left == parent)
		{
			parentparent->_left = subR;
		}
		else
		{
			parentparent->_right = subR;
		}
	}
	else
	{
		_root = subR;
	}
	subR->_parent = parentparent;
	subR->_bf = parent->_bf = 0;
}

(3)左右双旋转

我们先来观察一下需要这种操作的情况:

情况1:

新节点插入在9的左边,首先以6节点为parent开始左旋转,然后就可以以10节点为parent开始右旋转,达到平衡

情况2:

新节点插入在8的右边,首先以6节点为parent开始左旋转,然后就可以以10节点为parent开始右旋转,达到平衡

情况3:

新节点插入在6的右边,首先以6节点为parent开始左旋转,然后就可以以10节点为parent开始右旋转,达到平衡

根据上面的三种情况,我们可以总结出一个规律:新节点都是插入在subL的右子树(subLR),我们可以根据subL是否为1来判断是否需要左右旋转

所以我们可以将所有情况抽象成一幅图:

操作步骤:

1.以subL为parent进行左旋转

2.以原parent来进行右旋转

3.更新各处因子:原parent可能为1或者0,subL可能为0或者-1(根据插入到subLR的左子树还是右子树),subLR最终都为0。注意:要在旋转前拿到subLR的因子才能判断

cpp 复制代码
void RotateLR(Node*parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;//要先拿
	RotateL(parent->_left);
	RotateR(parent);//subLR的因子已经更新好了
	//更新因子
	if (bf == 0)//subLR两边都有
	{
		subL->_bf = 0;
		parent->_bf = 0;
	}
	else if (bf == 1)
	{
		subL->_bf = -1;
		parent->_bf = 0;
	}
	else
	{
		subL->_bf = 0;
		parent->_bf = 1;
	}

}

(4)右左双旋转

右左旋转和左右旋转完全类似。也可以分为三种情况(就不过多详细解释了),我们直接来看总结:新节点都是插入在subL的右子树(subRL),我们可以根据subR是否为1来判断是否需要右左旋转

所以我们可以将所有情况抽象成一幅图:

操作步骤:

1.以subR为parent进行右旋转

2.以原parent来进行左旋转

3.更新各处因子:原parent可能为-1或者0,subL可能为0或者1(根据插入到subRL的左子树还是右子树),subRL最终都为0。注意:要在旋转前拿到subRL的因子才能判断

cpp 复制代码
void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;
	RotateR(parent->_right);
	RotateL(parent);
	//更新因子
	if (bf == 0)
	{
		subR->_bf = 0;
		parent->_bf = 0;
	}
	else if (bf == 1)
	{
		subR->_bf = 0;
		parent->_bf = -1;
	}
	else
	{
		subR->_bf = 1;
		parent->_bf = 0;
	}
}

我们来看具体insert的代码:

cpp 复制代码
bool insert(const pair<K, V>&kv)
{
	Node* newnode = new Node(kv);
	if (_root == nullptr)
	{
		_root = newnode;
		return true;
	}
	Node* cur = _root;
	Node* parent = nullptr;
	while (cur)
	{
		if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}
	//插入
	if (parent->_kv.first < kv.first)
	{
		parent->_right = newnode;
	}
	else
	{
		parent->_left = newnode;
	}
	newnode->_parent = parent;
	//平衡因子
	cur = newnode;
	while (parent)
	{
		if (parent->_left == cur)
		{
			parent->_bf--;
		}
		else
		{
			parent->_bf++;
		}
		if (parent->_bf == 0)
		{
			break;
		}
		else if(parent->_bf==1||parent->_bf==-1)
		{
			cur = parent;
			parent = parent->_parent;
		}
		else if (parent->_bf == 2 || parent->_bf == -2)
		{
			if (parent->_left!=nullptr&&parent->_left->_bf == -1)
			{
				
				RotateR(parent);
				
				break;
			}
			if (parent->_left != nullptr&&parent->_left->_bf == 1)
			{
				
				RotateLR(parent);
				
				break;
			}
			if (parent->_right != nullptr&&parent->_right->_bf == 1)
			{
				
				RotateL(parent);
				
				break;
			}
			if (parent->_right != nullptr&&parent->_right->_bf == -1)
			{
				
				RotateRL(parent);
				break;
			}
		}
		else
		{
			assert(false);
		}
	}
	return true;

}

3.3查找------find

cpp 复制代码
Node* find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < key)
		{
			cur = cur->_right;
		}
		else if (cur->_kv.first > key)
		{
			cur = cur->_left;
		}
		else
		{
			return cur;
		}
	}
	return nullptr;
}

3.4判断是否为AVL树

运用:子函数+高度遍历

cpp 复制代码
int Hight(Node* root)
{
	if (root == nullptr)
	{
		return 0;
	}
	int righthight = Hight(root->_right);
	int lefthight = Hight(root->_left);
	return righthight > lefthight ? righthight + 1 : lefthight + 1;
}
bool Is_AVLTree(Node* root)
{
	if (root == nullptr)
	{
		return true;
	}
	int righthight = Hight(root->_right);
	int lefthight = Hight(root->_left);
	int diff = righthight - lefthight;
	if (diff <= -2 || diff >= 2)
	{
		cout << "平衡失常" << endl;
		return false;
	}
	if (root->_bf != diff)
	{
		cout << "平衡因子失常" << endl;
		return false;
	}
	return Is_AVLTree(root->_left) && Is_AVLTree(root->_right);
}
bool test_Is_AVLTree()
{
	return Is_AVLTree(_root);
}

3.5前序打印

cpp 复制代码
void AVLPREorder()
{
	PREorder(_root);
}
void PREorder(Node* root)
{
	if (root == nullptr)
	{
		return;
	}
	cout << root->_kv.first << " ";
	//cout << endl;
	PREorder(root->_left);
	PREorder(root->_right);
}

完整代码:

头文件:AVL.h

cpp 复制代码
#pragma once
#include<iostream>
#include<utility>
#include<assert.h>
#include<vector>
using namespace std;
template<class K,class V>
struct AVLTreeNode
{
	AVLTreeNode(const pair<K,V>&kv)
		:_kv(kv)
		, _parent(nullptr)
		, _left(nullptr)
		, _right(nullptr)
		,_bf(0)
	{}
	pair<K, V>_kv;
	AVLTreeNode<K, V>* _parent;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	int _bf;

};
template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	AVLTree() = default;
	bool insert(const pair<K, V>&kv)
	{
		Node* newnode = new Node(kv);
		if (_root == nullptr)
		{
			_root = newnode;
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		//插入
		if (parent->_kv.first < kv.first)
		{
			parent->_right = newnode;
		}
		else
		{
			parent->_left = newnode;
		}
		newnode->_parent = parent;
		//平衡因子
		cur = newnode;
		while (parent)
		{
			if (parent->_left == cur)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
			if (parent->_bf == 0)
			{
				break;
			}
			else if(parent->_bf==1||parent->_bf==-1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				if (parent->_left!=nullptr&&parent->_left->_bf == -1)
				{
					
					RotateR(parent);
					
					break;
				}
				if (parent->_left != nullptr&&parent->_left->_bf == 1)
				{
					
					RotateLR(parent);
					
					break;
				}
				if (parent->_right != nullptr&&parent->_right->_bf == 1)
				{
					
					RotateL(parent);
					
					break;
				}
				if (parent->_right != nullptr&&parent->_right->_bf == -1)
				{
					
					RotateRL(parent);
					break;
				}
			}
			else
			{
				assert(false);
			}
		}
		return true;

	}
	//右旋
	void RotateR(Node*parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}
		Node* parentparent = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;
		if (parentparent)
		{
			if (parentparent->_left == parent)
			{
				parentparent->_left = subL;
			}
			else
			{
				parentparent->_right = subL;
			}
		}
		else
		{
			_root = subL;
		}
		subL->_parent = parentparent;
		//更新因子
		subL->_bf = parent->_bf = 0;
	}
	//左旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		parent->_right = subRL;
		if (subRL)
		{
			subRL ->_parent = parent;
		}
		Node* parentparent = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;
		if (parentparent)
		{
			if (parentparent->_left == parent)
			{
				parentparent->_left = subR;
			}
			else
			{
				parentparent->_right = subR;
			}
		}
		else
		{
			_root = subR;
		}
		subR->_parent = parentparent;
		subR->_bf = parent->_bf = 0;
	}
	//左右旋
	void RotateLR(Node*parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;
		RotateL(parent->_left);
		RotateR(parent);//subLR的因子已经更新好了
		//更新因子
		if (bf == 0)//subLR两边都有
		{
			subL->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else
		{
			subL->_bf = 0;
			parent->_bf = 1;
		}

	}
	//右左旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;
		RotateR(parent->_right);
		RotateL(parent);
		//更新因子
		if (bf == 0)
		{
			subR->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subR->_bf = 0;
			parent->_bf = -1;
		}
		else
		{
			subR->_bf = 1;
			parent->_bf = 0;
		}
	}
	Node* find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}
	int Hight(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		int righthight = Hight(root->_right);
		int lefthight = Hight(root->_left);
		return righthight > lefthight ? righthight + 1 : lefthight + 1;
	}
	bool Is_AVLTree(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		int righthight = Hight(root->_right);
		int lefthight = Hight(root->_left);
		int diff = righthight - lefthight;
		if (diff <= -2 || diff >= 2)
		{
			cout << "平衡失常" << endl;
			return false;
		}
		if (root->_bf != diff)
		{
			cout << "平衡因子失常" << endl;
			return false;
		}
		return Is_AVLTree(root->_left) && Is_AVLTree(root->_right);
	}
	bool test_Is_AVLTree()
	{
		return Is_AVLTree(_root);
	}
	void AVLPREorder()
	{
		PREorder(_root);
	}
	void PREorder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		cout << root->_kv.first << " ";
		//cout << endl;
		PREorder(root->_left);
		PREorder(root->_right);
	}
private:
	Node* _root = nullptr;
};

测试文件:test.cpp

cpp 复制代码
void test01()
{
	vector<int> R({ 10,12,7,5,8,4,3 });
	vector<int> L({ 10,15,6,20,14,25 });
	vector<int> vv({ 16, 3, 7, 11, 9, 26, 18, 14, 15 });
	AVLTree<int, int> tree;
	for (auto e : vv)
	{
		tree.insert({e,e});
	}
	tree.AVLPREorder();


	if (tree.test_Is_AVLTree())
	{
		cout << "平衡" << endl;
	}
}
相关推荐
天若有情6732 小时前
通用个性化推荐核心架构思路:从视频到电商的跨场景落地实践
人工智能·算法·架构·推流·个性化推荐·猜你喜欢
s09071362 小时前
【声纳成像】基于滑动子孔径与加权拼接的条带式多子阵SAS连续成像(MATLAB仿真)
开发语言·算法·matlab·合成孔径声呐·后向投影算法·条带拼接
jay神2 小时前
基于YOLOv8的钢材表面缺陷检测系统
人工智能·算法·yolo·目标检测·计算机视觉
Accerlator2 小时前
2026年3月21日刷题
算法
2401_891655812 小时前
此电脑网络位置异常的AD域排错指南的技术文章大纲
开发语言·python·算法
DLGXY2 小时前
STM32(二十七)——独立看门狗&窗口看门狗
stm32·嵌入式硬件·算法
不要秃头的小孩2 小时前
50. 随机数排序
数据结构·python·算法
tankeven2 小时前
HJ139 小红的01子序列计数(hard)
c++·算法
weixin_649555672 小时前
C语言程序设计第四版(何钦铭、颜晖)第十章函数与程序设计之汉诺塔问题
c语言·c++·算法