C++进阶-->AVL树的实现

1. AVL树的介绍

1、AVL树的名字来源于他的发明者G. M. Adelson-Velsky和E. M. Landis两个前苏联的科学家,他们名字首元素组成。

2、AVL树就是我们前面二叉搜索树实现的时候提到的平衡二叉搜索树即二叉搜索树的左右孩子都是AVL树,即左右子树的高度差的绝对值不超过1。AVL树是一棵高度平衡二叉搜索树,通过高度差来控制平衡,从而引入了一个关键词--平衡因子。

3、每个结点都有平衡因子,平衡因子的计算方法为:右子树的高度减去左子树的高度,其实左子树减去右子树也可以,只不过后面的实现的一些逻辑关系需要变化一下。且任何的平衡因子的值都为0/1/-1,如果为2/-2则需要进行旋转操作。AVL树的实现并不一定需要平衡因子,但有了平衡因子我们就可以观察他的高度变化从而对不同情况进行不同操作。

4、有人可能疑惑,为什么AVL树的左右子树高度差绝对值不超过1,为什么不能为0,那我们想想看,会不会有些场景是无法做到高度差不超过0的,如下图所示:

5、AVL树整体结构和完全二叉树类似,因为左右子树高度差不超过1,那么就说明只有最后一行是不完整的;且树的整体高度可以控制在logN,增删查改也为O(logN)。


2. AVL树的实现

2.1 AVL树大体框架的设计

因为AVL树是key_value结构的,那么我们就需要定义一个pair键值对进行存储key和value的值,然后还有个存储平衡因子(balance factor)的值的变量,还需要一个找到parent的指针;以下是代码实现。

树结点的代码:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

//实现一个key_val结构
template<class K, class V>
//实现树结点结构
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	//平衡因子
	int _bf;

	AVLTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
	{}
};

树的结构的代码:

//实现 树的结构
template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	
private:
	Node* _root = nullptr;
};

2.2 AVL树的插入insert实现

2.2.1 AVL树插入大概思路

1、插入和前面二叉搜索树一样,比当前父亲节点的值大就往右走,反则往左走。

2、重点不在插入,而是在更新平衡因子,并且新增结点后,只会影响插入结点的全部祖先或者是部分祖先的平衡因子,例如插入的值对于_root结点来说是右子树,那么左子树的平衡因子不会受到影响。

3、更新平衡因子过程如果出现不平衡,那就需要进行旋转,我们前面说了AVL树的左右子树高度差不超过1,那么如果超过1即为2的时候就出现不平衡,所以要进行旋转。

2.2.2 更新平衡因子

更新规则:

1、平衡因子 = 右子树的高度 - 左子树的高度

2、只有子树高度变化才会影响当前结点的平衡因子

3、插入结点在当前结点(parent结点)的左子树,那么当前结点(parent结点)的bf--,新增结点在当前结点(parent结点)的右子树,那么当前结点(parent结点)的平衡因子bf++。

4、parent的平衡因子的变化决定是否需要继续往上面的祖先的平衡因子进行修改。

更新停止条件:

1、如果parent的平衡因子等于0,那么说明当前parent的左右子树高度差为0,意味高度相等,树平衡,所以不需要再网上更新,举个例子,一开始树是一边高一边低,然后低的那一边增加了结点,即高度增加了,但他们原本的高度就是以最高的高度为标准,所以低的那边高度增加了不影响树的高度。

2、更新后如果parent的平衡因子等于1或者-1,则说明更新前parent的平衡因子的值是0,也意味着更新前以parent为root的树本身是平衡的,左右子树的高度一致,更新后树的整体高度发生变化,那就需要往上判断祖先的左右子树的整体高度有无变化,没有变化则不需要在往上走,如果有变化则需要接着往上看祖先的祖先的平衡因子。

3、更新后如果parent的平衡因子为2或者-2,说明本身树的高度就是一边高一边低,但是新增的结点在高的子树处,那就意味着高的更高了,本身树的高度差为1或-1,但现在为2或者-2,这不符合AVL树的特性(即左右子树的高度差不超过1或者-1)就需要进行旋转。(旋转下面讲这里提一下。)


代码实现如下,重点在“”分割线处,上面的部分代码是前面二叉搜索树所实现过的,具体思路可以翻一下前面那一篇博客:

	bool insert(const pair<K, V>& kv)
	{
		//判断树是否为空
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		//树不为空,找对应的位置进行插入
		Node* parent = nullptr;
		Node* cur = _root;
		
		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;
			}
		}
		//找到可以插入的位置cur了
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

/
		//插入完毕,开始对平衡因子进行修改
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
			//对parent的平衡因子修改完毕,也有可能对祖先的平衡因子有影响,那么我们就需要
			//判断parent的平衡因子的变化情况再对parent的parent进行修改

			//bf等于0说明平衡了。
			if (parent->_bf == 0)
			{
				break;
			}

			//发现是1或者-1,那就说明bf是从0变过来的,那就意味着高度发生变化,那么就需要对
			//parent的parent进行修改
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}

			//为2了,不为AVL树的结构,就需要开始旋转
			//旋转在下面会讲。
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				//旋转
				break;
			}

			//这个是为了如果说_bf给出一些奇怪的值的时候就在这里直接断掉。
			else
			{
				assert(false);
			}
		}
		return true;
	}

2.3 旋转

2.3.1 旋转的规则

1、保证二叉搜索树的规定没被改变,即比根结点大的放右边,比根结点小的放左边。

2、让旋转的树从不满足变平衡,降低旋转树的高度。

旋转分为右单旋、左单旋、左右双旋、右左双选

2.3.2 右单旋

要使用右单旋首先是因为左子树的高度比右子树的高度高,那么我们就需要让在右边的数据往下走,左边的数据往上走,这样高度就会平衡。给个图做实例:

这样旋转的本质其实就是让高度高的往上走,a的高度高,我们不可能让这个单一的子树往上走,所以我们只能移动他的根结点subL,就让subL成为这整棵树的根节点即可,那么a就会往上走,高度自然就降低了。

然后我们再看单纯是subL走到根节点处不行,我们还要让原来的根节点parent让出位置才可以,那我们分析,parent肯定是比左子树subL的子结点都大,那么我们就可以让parent走到subL的右子树处,但右子树还有一个subLR,我们再分析看看subLR能放到哪里,因为parent的值肯定比左子树subL所有的子结点都大,那么就可以让subLR放到parent的左边。这样做树的结构没有被破坏,还让高度平衡了。

最后我们还需要改变他们的平衡因子即可完成右单旋。

上图是抽象图,抽象图即可包含所有情况,这里就不举单一的情况出来例如h = 0的时候等等情况,因为在抽象图里都可以演示出来,代入即可,实现代码如下:

	//右旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;


		parent->_left = subLR;
		//首先判断subLR是否为空,不为空就让parent->left 指向它
		if (subLR)
		{
			subLR->_parent = parent;
		}

		//如果这棵树只是一个子树,那就需要保存上一个树结点
		Node* pParent = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;

		//如果这棵树不是某棵树的子树
		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}

		//如果是某棵树的子树
		else
		{
			//判断是在某棵树的左子树还是右子树
			//左子树
			if (pParent->_left == parent)
			{
				pParent->_left = subL;
			}

			//右子树
			else
			{
				pParent->_right = subL;
			}
			subL->_parent = pParent;
		}
		subL->_bf = 0;
		parent->_bf = 0;
	}

代码解析:

1、第一个if条件if(subLR)为空的时候,即h  = 0的时候,我们就不能对subL解引用,不然就会造成对空指针解引用,程序会崩溃。

2、定义一个pParent的用处是,如果说该图的树只是一棵子树,我们在进行旋转的时候改变了_root值从parent改成subL,那我们还需要让pParent的left或者right的指针指向subL,然后subL的parent指针指向pParent,如下图所示:

所以还要在下面进行pParent->_left ==parent或者pParent->_right==parent的判断。

3、最后就是更新平衡因子。


2.3.3 左单旋

左单旋的实现步骤也是一样,我们这里就不过多讲述,直接上图和代码,只要理解了右单旋,也就可以搞定左单旋。同样的思路,因为右子树的高度大于左子树的高度,那么我们就需要让右子树往上走(这个是我自己总结出来的思路方便我自己记忆hhhh)然后parent的那一棵树就需要往下走。

	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 == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subR;
			}
			else
			{
				parentParent->_right = subR;
			}
			subR->_parent = parentParent;
		}

		parent->_bf = subR->_bf = 0;
	}

代码可能和右旋有一点点不一样,在右单旋处pParent就是parentParent的缩写。

还有一点就是第二个if判断条件,右单旋是判断parent是否是root,而左单旋是判断parentParent是否为nullptr,其实实质上是一样的,如果parent为root的话,那么parentParent肯定是nullptr,如果parent不为root的话就说明parent整棵树是一棵子树,那么就肯定有parent结点,所以parentParent不为空。

最后就是通过图观察发现使用右单旋的时候parent的bf为-2,cur的bf为-1,使用左单旋的时候parent的bf等于2,cur的bf等于1,那么这在旋转的时候也还要判断一下,因为他们的使用条件不一样。


2.3.4 左右双旋

左右双旋顾名思义左旋再右旋,但为什么需要这个呢?我们用一个例子进行解析;

我们看到下面那一组图,发现左子树高,使用右单旋发现还是不平衡,并且变成了右子树高。我们观察发现,要使用右单旋的时候必须是单纯的一边高,由上面那一组图可以看见,右单旋是可以平衡树的,而下面一组图不是单纯的一边高。这时候就需要左右双旋进行解决。

左右双旋的旋转步骤:

由上面分析我们可以看出,使用单旋必须是一边高,那么我们可以构造一个一边高出来,假设没有10这个结点,5就是root结点,这样就是一边高了,右边高对5使用左单旋,完成后就发现,我们以10为_root的方面看,这棵树又成了左边一边高,那么我们只需要对10进行右单旋即可完成平衡。

这里添加null是为了更方便观察,因为我刚学的时候对于这个结构进行左右双旋真的很头疼,因为需要凭空想象出一个结点来进行单旋,那倒不如直接画几个null结点在上面更方便观察。所以左右双旋就这样完成了,最后还剩平衡因子没有解决。

解决平衡因子更新问题:

双旋的旋转操作简单,但是更新平衡因子才是难点,我们需要分多个类进行讨论,我们先以两个场景来引出这个问题。

由上三个场景我们可以看出,插入数据在8的左边和右边、还有插入8本身,他们的平衡因子都会因此改变。我们观察发现,场景1、2、3的8的位置的平衡因子不同,10和5的平衡因子也会跟着受影响,所以说我们要根据8的平衡因子来确定10和5的平衡因子的更新规律。

设:10为parent、5为subL、8为subLR(我忘记在图上画了,偷个懒)

场景一:当subLR->bf==1的时候,双旋后: subLR->bf== 0 、subL->bf== -1 、parent->bf ==0;

场景二:当subLR->bf ==-1的时候,双旋后: subLR->bf== 0 、subL->bf== 0 、parent->bf ==-1;

场景三:当subLR->bf ==0的时候,双旋后: subLR->bf== 0 、subL->bf== 0 、parent->bf ==0;

完成平衡因子更新问题后就可以实现左右双旋了。总结实现左右双旋的两个步骤:1、进行双旋

2、进行平衡因子的更新。实现代码如下可见:

	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

		RotateL(parent->_left);
		RotateR(parent);

		if (bf == -1)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

2.3.5 右左双旋

右左双旋的实现逻辑也一样,这里就不过多讲述了上代码:

	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;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

最后补充一点:就是为什么加一个else语句然后assert(false),这样是避免如果bf的值不为0不为1不为-1的时候直接报错,那么我们就知道问题就是出在bf的值身上。


2.3.6 完善insert

可能有人会注意到2.2处的insert的实现是不完整的,因为那里还没有讲旋转,这里我们讲完了之后我们可以一一观察,根据parent和cur的平衡因子的不同来确定是使用右单旋、左单旋、还是左右双旋、还是右左双旋。

右单旋:parent的平衡因子等于-2,cur的平衡因子等于-1。

左单旋:parent的平衡因子等于2,cur的平衡因子等于1。

左右单旋:parent的平衡因子等于-2,cur的平衡因子等于1。

右单旋:parent的平衡因子等于2,cur的平衡因子等于-1。

这些都是可以根据上面画的图来分析出来的。这里就直接上完整版的insert的代码了:

	bool Insert(const pair<K, V>& kv)
	{
		//判断树是否为空
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		//树不为空,找对应的位置进行插入
		Node* parent = nullptr;
		Node* cur = _root;
		
		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;
			}
		}
		//找到可以插入的位置cur了
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		//插入完毕,开始对平衡因子进行修改
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
			//对parent的平衡因子修改完毕,也有可能对祖先的平衡因子有影响,那么我们就需要
			//判断parent的平衡因子的变化情况再对parent的parent进行修改

			//bf等于0说明平衡了。
			if (parent->_bf == 0)
			{
				break;
			}

			//发现是1或者-1,那就说明bf是从0变过来的,那就意味着高度发生变化,那么就需要对
			//parent的parent进行修改
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}

			//为2了,不为AVL树的结构,就需要开始旋转
			//旋转在下面会讲。
			else if (parent->_bf == 2 || parent->_bf == -2)
			{

				//右单旋
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}
				//左单旋
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);
				}
				//左右双旋
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				//右左双旋
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}
				else
				{
					assert(false);
				}
				break;
			}

			//这个是为了如果说_bf给出一些奇怪的值的时候就在这里直接断掉。
			else
			{
				assert(false);
			}
		}
		return true;
	}

3.测试代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<vector>
#include"AVLTree.h"

void TestAVLTree1()
{
	AVLTree<int, int> t;
	// 常规的测试用例
	int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	// 特殊的带有双旋场景的测试用例
	//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };

	for (auto e : a)
	{
		t.Insert({ e, e });
	}

	t.InOrder();
	cout << t.IsBalanceTree() << endl;
}

// 插入一堆随机值,测试平衡,顺便测试一下高度和性能等
void TestAVLTree2()
{
	const int N = 1000000;
	vector<int> v;
	v.reserve(N);
	srand(time(0));
	for (size_t i = 0; i < N; i++)
	{
		v.push_back(rand() + i);
	}

	size_t begin2 = clock();
	AVLTree<int, int> t;
	for (auto e : v)
	{
		t.Insert(make_pair(e, e));
	}
	size_t end2 = clock();

	cout << "Insert:" << end2 - begin2 << endl;
	cout << t.IsBalanceTree() << endl;

	cout << "Height:" << t.Height() << endl;
	cout << "Size:" << t.Size() << endl;

	size_t begin1 = clock();
	// 确定在的值
	for (auto e : v)
	{
		t.Find(e);
	}
	// 随机值
	/*for (size_t i = 0; i < N; i++)
	{
		t.Find((rand() + i));
	}*/
	size_t end1 = clock();
	cout << "Find:" << end1 - begin1 << endl;
}


int main()
{
	TestAVLTree2();

	return 0;
}

4. 完整的AVL树实现的代码

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

//实现一个key_val结构
template<class K, class V>
//实现树结点结构
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	//平衡因子
	int _bf;

	AVLTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
	{}
};

//实现 树的结构
template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	//insert
	bool Insert(const pair<K, V>& kv)
	{
		//判断树是否为空
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		//树不为空,找对应的位置进行插入
		Node* parent = nullptr;
		Node* cur = _root;
		
		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;
			}
		}
		//找到可以插入的位置cur了
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		//插入完毕,开始对平衡因子进行修改
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
			//对parent的平衡因子修改完毕,也有可能对祖先的平衡因子有影响,那么我们就需要
			//判断parent的平衡因子的变化情况再对parent的parent进行修改

			//bf等于0说明平衡了。
			if (parent->_bf == 0)
			{
				break;
			}

			//发现是1或者-1,那就说明bf是从0变过来的,那就意味着高度发生变化,那么就需要对
			//parent的parent进行修改
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}

			//为2了,不为AVL树的结构,就需要开始旋转
			//旋转在下面会讲。
			else if (parent->_bf == 2 || parent->_bf == -2)
			{

				//右单旋
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}
				//左单旋
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);
				}
				//左右双旋
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				//右左双旋
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}
				else
				{
					assert(false);
				}
				break;
			}

			//这个是为了如果说_bf给出一些奇怪的值的时候就在这里直接断掉。
			else
			{
				assert(false);
			}
		}
		return true;
	}

	//右旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;


		parent->_left = subLR;
		//首先判断subLR是否为空,不为空就让parent->left 指向它
		if (subLR)
		{
			subLR->_parent = parent;
		}

		//如果这棵树只是一个子树,那就需要保存上一个树结点
		Node* pParent = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;

		//如果这棵树不是某棵树的子树
		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}

		//如果是某棵树的子树
		else
		{
			//判断是在某棵树的左子树还是右子树
			//左子树
			if (pParent->_left == parent)
			{
				pParent->_left = subL;
			}

			//右子树
			else
			{
				pParent->_right = subL;
			}
			subL->_parent = pParent;
		}
		subL->_bf = 0;
		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 == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subR;
			}
			else
			{
				parentParent->_right = subR;
			}
			subR->_parent = parentParent;
		}

		parent->_bf = subR->_bf = 0;
	}
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

		RotateL(parent->_left);
		RotateR(parent);

		if (bf == -1)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	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;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	int Height()
	{
		return _Height(_root);
	}

	int Size()
	{
		return _Size(_root);
	}

	bool IsBalanceTree()
	{
		return _IsBalanceTree(_root);
	}

	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;
	}

private:
	Node* _root = nullptr;

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}

	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	int _Size(Node* root)
	{
		if (root == nullptr)
			return 0;

		return _Size(root->_left) + _Size(root->_right) + 1;
	}

	bool _IsBalanceTree(Node* root)
	{
		// 空树也是AVL树
		if (nullptr == root)
			return true;
		// 计算pRoot结点的平衡因子:即pRoot左右子树的高度差
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		int diff = rightHeight - leftHeight;

		// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者
		// pRoot平衡因子的绝对值超过1,则一定不是AVL树
		if (abs(diff) >= 2)
		{
			cout << root->_kv.first << "高度差异常" << endl;
			return false;
		}

		if (root->_bf != diff)
		{
			cout << root->_kv.first << "平衡因子异常" << endl;
			return false;
		}

		// pRoot的左和右如果都是AVL树,则该树一定是AVL树
		return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
	}
};

END!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值