C++进阶——AVL树

目录

一、概念

二、模拟实现

1.平衡因子

2.搭建框架

3.插入

4.旋转——处理平衡因子为2或-2的情况

三、旋转原理及实现

1.右单旋

右单旋的代码实现:

2.左单旋

左单旋的代码实现

3.左右双旋

左右双旋的代码实现

4.右左双旋

右左双旋的代码实现

5.插入最终代码

四、测试


一、概念

AVL树,是平衡二叉搜索树的一种,以发明它的前苏联科学家的名字(G. M. Adelson-Velsky和E. M. Landis)命名。

1.AVL树是平衡二叉搜索树的一种,也就是说,每一个AVL树的左右子树的高度差都不超过1,且其左右子树也为AVL树

2.空树也是AVL树。

为什么AVL树的左右子树的高度差不能超过1而不是0呢,因为在某些情况下,平衡二叉搜索树做不到完全平衡,比如两个或者四个结点的情况,这种情况下是必然会出现左右子树的高度差的,因此只要我们把左右子树的高度差控制在1的范围内就好。

AVL树的实现其实有很多版本,本博客挑选了比较容易理解的平衡因子版本来进行探究和实现。

二、模拟实现

1.平衡因子

在AVL树的模拟实现中,我们在每个结点中增添了平衡因子这一成员变量,平衡因子在其中表示的含义是:右子树高度减左子树高度

平衡因子会出现的三种情况:

1. 如果bf(平衡因子,balance factor)等于0,说明该结点的左右子树高度相等,满足AVL树平衡条件;

2. 如果bf(平衡因子,balance factor)等于1或-1,说明该结点的左右子树的高度差为1,仍满足AVL树的平衡条件;

3. 如果bf(平衡因子,balance factor)等于2或-2,说明该结点的左右子树高度差达到了2,AVL树以已经不平衡了,需要我们做必要的调整(旋转,后面会重点说)。

既然结点中加入了平衡因子这个变量,那么就会涉及到平衡因子的更新,为了更加方便快速地更新平衡因子,我们还需要向结点中添加父结点这一成员变量。

2.搭建框架

只需要在前面实现的二叉搜索树的框架基础上,把增加一下结点的成员变量即可。

// key/value版本的AVLTree
template<class K, class V>
struct AVLTNode
{
	pair<K, V> _kv;
	int _bf; // balance factor 平衡因子
	AVLTNode<K, V>* _parent; // 父结点,方便快速更新平衡因子
	AVLTNode<K, V>* _left;
	AVLTNode<K, V>* _right;

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

template<class K, class V>
class AVLTree
{
	using Node = AVLTNode<K, V>;
public:


private:
	Node* _root = nullptr;
};

3.插入

我们先顺着来:

插入的第一步肯定还是需要查找,就是找到需要插入的位置,如果已经有这个结点了,返回false,如果没有,就插入。(同二叉搜索树)

bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}

	Node* cur = _root, * 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;
		}
	}
	cur = new Node(kv);
	if (parent->_kv.first > kv.first) parent->_left = cur;
	else parent->_right = cur;
	// 至此为普通二叉搜索树的插入
	// 不过由于我们的Node引入了额外的成员变量,所以还需要更新平衡因子和parent结点指针

}

到现在为止的操作都还是与二叉搜索树的操作一样的。

不过插入之后呢,由于我们结点的成员变量多出了平衡因子和父结点,所以这两个成员变量还需要我们去维护,父结点的维护就很简单了,在插入cur之后加一句cur->_parent = parent;就ok,下面是平衡因子的维护:

对平衡因子的更新,可以分为3种情况:

1. 平衡因子更新后为0:这种情况肯定是平衡因子从1或-1更新过来的,也就是原来该结点的左右子树高度差为1,更新后高度差为0,也就是说该结点的父结点对应的子树高度没有变化,所以就不必再继续更新。

2. 平衡因子更新后为1或-1:由于插入前该树是AVL树,所以平衡因子必定是从0变成的1或-1,那么该结点的父结点的对应子树高度会+1,要继续向上更新。

3. 平衡因子更新后为2或-2:这种情况肯定是平衡因子从1或-1更新来的,也就是说该结点的左右子树的高度差变成了2,此时该树就不是AVL树了,那么就应该对平衡因子为2的部分进行一定的操作(旋转)来解决。

注意:

1. 更新平衡因子最多会更新到根节点,不会更新到根节点的另外一条子树上。

2. 更新过程中,结点左子树插入了结点那么_bf--,右子树插入了结点_bf++

下面是维护父结点和更新平衡因子的版本(还没有旋转):

bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}

	Node* cur = _root, * 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;
		}
	}
	cur = new Node(kv);
	if (parent->_kv.first > kv.first) parent->_left = cur;
	else parent->_right = cur;
	// 至此为普通二叉搜索树的插入
	// 不过由于我们的Node引入了额外的成员变量,所以还需要更新平衡因子和parent结点指针

	// cur的父结点维护
	cur->_parent = parent;

	// 平衡因子的更新
	// parent是nullptr(cur是根结点)或者parent->_bf == 0时循环结束
	while (parent)
	{
		// 插入在parent右结点,parent的_bf++
		// 插入在parent左结点,parent的_bf--
		if (cur = parent->_right) parent->_bf++;
		else parent->_bf--;

		// 更新parent平衡因子后,进行判断
		// 继续向上更新/停止更新/旋转
		if (parent->_bf == 0)
		{
			// 停止更新,也不必做任何调整
			break;
		}
		else if (parent->_bf == 1 || parent->_bf == -1)
		{
			// 继续向上更新
			cur = parent;
			parent = cur->_parent;
		}
		else if (parent->_bf == 2 || parent->_bf == -2)
		{
			// 旋转
		}
		else
		{
			// 如果平衡因子的逻辑出错出现意料之外的值,就会直接报错
			assert(false);
		}
	}
}

4.旋转——处理平衡因子为2或-2的情况

 旋转分四种情况,这四种情况又可以分为两组,同一组之间的两个旋转比较类似(对称),下面我来解释一下这四种情况的产生:

首先我们需要一个空树,其实这个空树就是AVL树。

接着我们插入两个结点(一个根结点和一个孩子结点)(由于左右的对称性我就随便插入了)

此时,该树它仍是AVL树

接着插入第三个结点:

如果是这样,该树仍然平衡,仍然是AVL树

考虑一下另外的情况:

如果是这样子,很明显该树就不平衡了,就不是AVL树了 ,而这其实就是旋转的两种情况,其它两种情况与这两种情况完全对称:

为什么只是列举了插入三个结点的情况,却能代表所有?

因为该列举的树的根节点也有可能是其它结点的孩子结点 ,而列举的这些结点的前两个结点(也可以有左右子树,只不过画的为空树)在插入第三个结点前也保持着AVL树的规范。

如果不是很明白,我后面会分析很一般的情况。

至于这些旋转它们的命名,相信你看完原理之后自然会明白。

三、旋转原理及实现

1.右单旋

右单旋,也就是刚刚列举的这个:

一起分析一下比较一般的情况:

这个旋转的大致是5转到10,10转到后来的5的右子树位置,看起来就像整体向右旋转(其实是顺时针),所以叫右单旋。

结点的名称含义:
parent:当前AVL树(可能是局部)的根结点

subL:sub替补L左边,parent的左孩子结点

subLR:parent的左孩子结点的右孩子结点

右单旋的过程:

parent连接到subL的右孩子上,subLR连接到parent的左孩子上。

那么我们来实现代码~

等等......代码真的有那么简单吗?不!
别忘了,我们的Node中多出的成员变量,需要我们再分析一下平衡因子和parent的更新

从刚刚图上可以看得,parent和subL的平衡因子都变成了0,其余的a,b,c中的平衡因子不变(因为他们的平衡因子在刚插入还未旋转的时候就已经更新过了)

另外,该AVL树可能是一个AVL树的局部的一部分,所以该AVL树旋转后的根结点的parent也要更新。不仅如此,该结点的parent的左/右孩子结点也要更新。

右单旋的代码实现:

// 右单旋  rotate 旋转 right 右
void RotateR(Node* parent)
{
	// 连接
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	subL->_right = parent;
	parent->_left = subLR;

	// 更新平衡因子
	subL->_bf = 0;
	parent->_bf = 0;

	// 更新_parent
	subL->_parent = parent->_parent;
	parent->_parent = subL;
	if (subLR)	subLR->_parent = parent;

	// 根结点的情况
	if (parent == _root)	_root = subL;
	// 非根结点	 更新“看不见的”parent
	else if (parent == subL->_parent->_left)	subL->_parent->_left = subL;
	else subL->_parent->_right = subL;
}

2.左单旋

与右单旋对称,大家可以自行推导,我给出图供大家参考

左单旋的代码实现

// 左单旋
void RotateL(Node* parent)
{
	// 连接
	Node* subR = parent->_right;
	Node* subRL = subL->_left;
	subL->_left = parent;
	parent->_right = subRL;

	// 更新平衡因子
	subR->_bf = 0;
	parent->_bf = 0;

	// 更新_parent
	subR->_parent = parent->_parent;
	parent->_parent = subR;
	if (subRL)	subRL->_parent = parent;

	// 根结点的情况
	if (parent == _root)	_root = subR;
	// 非根结点	 更新“看不见的”parent
	else if (parent == subR->_parent->_left)	subR->_parent->_left = subR;
	else subR->_parent->_right = subR;
}

3.左右双旋

需要左右双旋的情况:

抽象情况:

 我们分解一下b:

由上面的分析我们可以知道,5和10都做不了当前局部AVL树的根节点,那么小于10但是大于5的结点8是否可以呢?

答案是可行,如果我们先以5为parent对其进行左单旋,就会变成:

再以10为parent对其进行右单旋:

现在得到的树就是AVL树了。

这里面需要注意的点:

插入结点在subLR的左子树还是右子树,这会影响到平衡因子的变化。

        如果插入结点在subLR的左子树,那么该结点会随第一次的左单旋接到subL的右子树上,subL的平衡因子为0,parent的平衡因子为1,反之subL的平衡因子为-1,parent的平衡因子为0。

        所以刚开始要判断一下subLR的平衡因子,以便后面平衡因子的更新。

第一次是左单旋,第二次是右单旋,所以叫左右双旋。 

左右双旋的代码实现

// 左右双旋
void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;
	RotateL(subL);
	RotateR(parent);
	if (bf == -1)
	{
		// 	不是RotateL和RotateR中已经把subL和subLR的_bf置为0了吗,为什么还要这样写
		// 减少函数之间的耦合度
		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);
	}
}

4.右左双旋

与左右双旋完全对称哦,大家自行研究~

右左双旋的代码实现

// 右左双旋
void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;
	RotateR(subR);
	RotateL(parent);
	if (bf == -1)
	{
		// 	不是RotateL和RotateR中已经把subL和subLR的_bf置为0了吗,为什么还要这样写
		// 减少函数之间的耦合度
		subRL->_bf = 0;
		subR->_bf = 1;
		parent->_bf = 0;
	}
	else if (bf == 1)
	{
		subRL->_bf = 0;
		subR->_bf = 0;
		parent->_bf = -1;
	}
	else if (bf == 0)
	{
		subRL->_bf = 0;
		subR->_bf = 0;
		parent->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

5.插入最终代码

在之前的插入代码实现中,就差最后的旋转就能完成了,我们只需要判断一下哪种情况需要哪个结点进行什么样的旋转,就ok了~

bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}

	Node* cur = _root, * 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;
		}
	}
	cur = new Node(kv);
	if (parent->_kv.first > kv.first) parent->_left = cur;
	else parent->_right = cur;
	// 至此为普通二叉搜索树的插入
	// 不过由于我们的Node引入了额外的成员变量,所以还需要更新平衡因子和parent结点指针

	// cur的父结点维护
	cur->_parent = parent;

	// 平衡因子的更新
	// parent是nullptr(cur是根结点)或者parent->_bf == 0时循环结束
	while (parent)
	{
		// 插入在parent右结点,parent的_bf++
		// 插入在parent左结点,parent的_bf--
		if (cur == parent->_right) parent->_bf++;
		else parent->_bf--;

		// 更新parent平衡因子后,进行判断
		// 继续向上更新/停止更新/旋转
		if (parent->_bf == 0)
		{
			// 停止更新,也不必做任何调整
			break;
		}
		else if (parent->_bf == 1 || parent->_bf == -1)
		{
			// 继续向上更新
			cur = parent;
			parent = cur->_parent;
		}
		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)
				RotateLR(parent);
			else if (parent->_bf == 2 && cur->_bf == 1)
				RotateL(parent);
			else if (parent->_bf == 2 && cur->_bf == -1)
				RotateRL(parent);
			else
				assert(false);
			break;
		}
		else
		{
			// 如果平衡因子的逻辑出错出现意料之外的值,就会直接报错
			assert(false);
		}
	}
	return true;
}

// 右单旋  rotate 旋转 right 右
void RotateR(Node* parent)
{
	// 连接
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	subL->_right = parent;
	parent->_left = subLR;

	// 更新平衡因子
	subL->_bf = 0;
	parent->_bf = 0;

	// 更新_parent
	subL->_parent = parent->_parent;
	parent->_parent = subL;
	if (subLR)	subLR->_parent = parent;

	// 根结点的情况
	if (parent == _root)	_root = subL;
	// 非根结点	 更新“看不见的”parent
	else if (parent == subL->_parent->_left)	subL->_parent->_left = subL;
	else subL->_parent->_right = subL;
}

// 左单旋
void RotateL(Node* parent)
{
	// 连接
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	subR->_left = parent;
	parent->_right = subRL;

	// 更新平衡因子
	subR->_bf = 0;
	parent->_bf = 0;

	// 更新_parent
	subR->_parent = parent->_parent;
	parent->_parent = subR;
	if (subRL)	subRL->_parent = parent;

	// 根结点的情况
	if (parent == _root)	_root = subR;
	// 非根结点	 更新“看不见的”parent
	else if (parent == subR->_parent->_left)	subR->_parent->_left = subR;
	else subR->_parent->_right = subR;
}

// 左右双旋
void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;
	RotateL(subL);
	RotateR(parent);
	if (bf == -1)
	{
		// 	不是RotateL和RotateR中已经把subL和subLR的_bf置为0了吗,为什么还要这样写
		// 减少函数之间的耦合度
		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(subR);
	RotateL(parent);
	if (bf == -1)
	{
		// 	不是RotateL和RotateR中已经把subL和subLR的_bf置为0了吗,为什么还要这样写
		// 减少函数之间的耦合度
		subRL->_bf = 0;
		subR->_bf = 1;
		parent->_bf = 0;
	}
	else if (bf == 1)
	{
		subRL->_bf = 0;
		subR->_bf = 0;
		parent->_bf = -1;
	}
	else if (bf == 0)
	{
		subRL->_bf = 0;
		subR->_bf = 0;
		parent->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

四、测试

主要需要测试一下我们实现的AVL树的平衡因子是否正常,高度差是否正常,以及验证一下我们实现的AVL树的插入和查找效率。

下面给出的是包含测试代码的整体本博客所有代码:

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

// key/value版本的AVLTree
template<class K, class V>
struct AVLTNode
{
	pair<K, V> _kv;
	int _bf; // balance factor 平衡因子
	AVLTNode<K, V>* _parent; // 父结点,方便快速更新平衡因子
	AVLTNode<K, V>* _left;
	AVLTNode<K, V>* _right;

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

template<class K, class V>
class AVLTree
{
	using Node = AVLTNode<K, V>;
public:
	// 查找(和二叉搜索树一样)
	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;
	}

	// 插入
	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		Node* cur = _root, * 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;
			}
		}
		cur = new Node(kv);
		if (parent->_kv.first > kv.first) parent->_left = cur;
		else parent->_right = cur;
		// 至此为普通二叉搜索树的插入
		// 不过由于我们的Node引入了额外的成员变量,所以还需要更新平衡因子和parent结点指针

		// cur的父结点维护
		cur->_parent = parent;

		// 平衡因子的更新
		// parent是nullptr(cur是根结点)或者parent->_bf == 0时循环结束
		while (parent)
		{
			// 插入在parent右结点,parent的_bf++
			// 插入在parent左结点,parent的_bf--
			if (cur == parent->_right) parent->_bf++;
			else parent->_bf--;

			// 更新parent平衡因子后,进行判断
			// 继续向上更新/停止更新/旋转
			if (parent->_bf == 0)
			{
				// 停止更新,也不必做任何调整
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				// 继续向上更新
				cur = parent;
				parent = cur->_parent;
			}
			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)
					RotateLR(parent);
				else if (parent->_bf == 2 && cur->_bf == 1)
					RotateL(parent);
				else if (parent->_bf == 2 && cur->_bf == -1)
					RotateRL(parent);
				else
					assert(false);
				break;
			}
			else
			{
				// 如果平衡因子的逻辑出错出现意料之外的值,就会直接报错
				assert(false);
			}
		}
		return true;
	}

	// 右单旋  rotate 旋转 right 右
	void RotateR(Node* parent)
	{
		// 连接
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		subL->_right = parent;
		parent->_left = subLR;

		// 更新平衡因子
		subL->_bf = 0;
		parent->_bf = 0;

		// 更新_parent
		subL->_parent = parent->_parent;
		parent->_parent = subL;
		if (subLR)	subLR->_parent = parent;

		// 根结点的情况
		if (parent == _root)	_root = subL;
		// 非根结点	 更新“看不见的”parent
		else if (parent == subL->_parent->_left)	subL->_parent->_left = subL;
		else subL->_parent->_right = subL;
	}

	// 左单旋
	void RotateL(Node* parent)
	{
		// 连接
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		subR->_left = parent;
		parent->_right = subRL;

		// 更新平衡因子
		subR->_bf = 0;
		parent->_bf = 0;

		// 更新_parent
		subR->_parent = parent->_parent;
		parent->_parent = subR;
		if (subRL)	subRL->_parent = parent;

		// 根结点的情况
		if (parent == _root)	_root = subR;
		// 非根结点	 更新“看不见的”parent
		else if (parent == subR->_parent->_left)	subR->_parent->_left = subR;
		else subR->_parent->_right = subR;
	}

	// 左右双旋
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;
		RotateL(subL);
		RotateR(parent);
		if (bf == -1)
		{
			// 	不是RotateL和RotateR中已经把subL和subLR的_bf置为0了吗,为什么还要这样写
			// 减少函数之间的耦合度
			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(subR);
		RotateL(parent);
		if (bf == -1)
		{
			// 	不是RotateL和RotateR中已经把subL和subLR的_bf置为0了吗,为什么还要这样写
			// 减少函数之间的耦合度
			subRL->_bf = 0;
			subR->_bf = 1;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == 0)
		{
			subRL->_bf = 0;
			subR->_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);
	}



private:
	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);
	}

private:
	Node* _root = nullptr;
};
// Test.cpp
#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;
}

okkkk,以上就是本博客的全部内容啦~

完结撒花~~~~~~~~~~~

໒( = ᴥ =)ʋ

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值