二叉树进阶(AVLTree)

目录

1.AVLTree概念

2.AVLTree模拟实现

2.1 AVLTree节点

2.2 插入实现基本框架

2.3 左单旋

2.4 右单旋

2.5 LR双旋

2.6 RL双旋

2.7 AVLTree树验证


1.AVLTree概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查

找元素相当于在一个链表中搜索元素,效率低下

于是有两位俄罗斯科学家提出了AVLTree的概念,来解决单支树的问题

在向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要

对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度

可以证明,如果它有n个结点,其高度可保持在 O(logN),搜索时间复杂度也是O(logN)

这里直接给出AVL树(高度平衡搜索二叉树)的定义

1.每个节点的左右子树高度之差的绝对值小于等于1

2.左右子树都为AVL树

2.AVLTree模拟实现

2.1 AVLTree节点

AVLTree实现的方法很多,这里采用的是平衡因子的实现方法

平衡因子是指每个节点右子树高度减去左子树高度的值

通过在每个节点设置一个balance factor,简称bf,就可以将代码简化,方便AVLTree之后的建树过程

template <class K,class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	//每个节点中还存一个指向父节点的指针
	AVLTreeNode<K, V>* _parent;
	pair<K, V> _kv;
	int _bf; //balance factor

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

2.2 插入实现基本框架

AVL树,是一棵高度平衡二叉搜索树

先是二叉搜索树,再是平衡

所以说建一棵AVL树,实际上和建一棵二叉搜索树的步骤完全一样,只不过需要考虑平衡因子的问

题,一旦平衡因子不对劲,我们就要相应作出调整,从而实现高度平衡

那现在往一棵树插入新节点,我们肯定就需要更新平衡因子

这里就只会出现两种情况

第一,更新完以后,平衡因子没有出现问题,即bf绝对值小于等于1,那平衡结果就没有受影响,不需要处理

第二,更新完以后,平衡出现问题,即bf绝对值大于1,那就要作相应的旋转处理

比如我们往下面这棵树插入一个新的节点10,9的平衡因子就肯定需要调整,由原来的0变成1

相应的8的平衡因子也需要调整,由原来的1,变为2

但是我们可以注意到的是,插入新增节点,只会影响祖先的平衡因子,也就是一条路径上的点,而

不是同一条路径的节点平衡因子是不会受到影响的

像上面树中的2,插入新节点10,对于2来说,是没有任何影响的,平衡因子还是2,别人只是吃瓜

群众,别人诛九族也轮不到它

插入新增节点后,它的父亲节点的bf肯定是需要改变的,插入在右边,那bf就加1;插入在左边,

那bf就减1,像图中的父亲节点9,新增节点插入在它的右边,bf就加1,从原来的0变为1

那爷爷是否需要调整呢?或者更往上的祖先节点的bf是否需要调整呢?

这就需要分情况讨论,判断是否需要往上继续更新,大白话来说,就是看祖先节点的左右子树是否

有变化?变了,肯定就需要更新,没变,就不需要更新

像图中新插入节点10来说,对于8这个节点,右子树高度变了,由1变2,另外一边的左子树的高度

没变,还是0,那自然它的bf就需要改变

但实操上,并不需要这么麻烦,直接看父节点调整完后的bf即可

因为父节点就在爷爷节点的左子树或者右子树之中

假如父节点调整完后的bf为0,说明之前一边高一边低,插入的新增节点刚好填上矮的那边,高度

没有变,那爷爷节点的bf肯定也不用变化,因为子树高度就没有变

那假如父节点调整完后的bf为1或者-1,说明插入前左右高度相等,现在有一边高,子树的高度改

变了,那对应爷爷节点的bf肯定也要变化

而最复杂的情况就是bf为2或者-2,此时子树就需要旋转调整,来保持仍然为一棵AVL树

至此,我们已经可以搭出插入的基本框架

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 = new Node(kv);
		if (parent->_kv.first > kv.first)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

		// 更新平衡因子
		while (parent)
		{    
            //只要插入新节点,其父节点的bf肯定会改变
            //在右边就加1,在左边就减1
			if (cur == parent->_right)
			{
				parent->_bf++;
			}
			else
			{
				parent->_bf--;
			}
            
            //判断祖先节点的bf是否还需要改变
			if (parent->_bf == 1 || parent->_bf == -1)
			{
				// 继续更新
				parent = parent->_parent;
				cur = cur->_parent;
			}
			else if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				

			    // 需要旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度
			    if (parent->_bf == 2 && cur->_bf == 1)
			    {
				    RotateL(parent);
		    	}
			    else 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)
			    {
			    	RotateRL(parent);
		    	}
			    else
			    {   
                    //能来到这里,说明树之前就出了问题,本身就不是一棵AVL树
			    	assert(false);
		    	}
		}

		return true;
	}

现在我们差的就是旋转处理的实现,目的就是

让这棵子树重新平衡,同时降低这棵子树的高度 

在父节点的bf == 2或者bf == -2的前提下,我们可以单独将这棵子树抽离出来分析,可以分为两种

情况

bf == 2意味着新插入的节点在右子树

 bf == -2则意味着新插入的节点在左子树

但是情况还没有这么简单,新增的节点还可能插入在右子树的右边,或者是左边;在左子树的左边

或者是右边,这四种情况的旋转方式是不同的,具体原因,在后面分析的时候,我们就会有更深的

体会

2.3 左单旋

旋转处理目的是

让这棵子树重新平衡,同时降低这棵子树的高度

现在我们抽象出来这样一个右高左低的模型,调整为怎么样的模型,是降低这棵子树的高度呢? 

 答案是将它折下来,将中间的节点提上来,在保持它仍然是一棵搜索二叉树情况下,形成一个倒v

有了前面的铺垫,我们就可以看具体左单旋的场景(PS:注意此时的30和60这两个数字并不是绝对

意义上的60和30,只是说明60这个节点的值要大于30这个节点的值)

若把30称作parent节点,60称作为subR节点,很容易发现,当往c这棵子树新插入节点的时候,

parent对应的bf就由原来的1变为2,subR(cur)就由原来的0变为1,符合我们之前所定义的需要右

旋的情况

 那具体如何旋转呢?就是按照倒v模型

1.b变成30的右边

2.30变成60的左边

60变成整棵树的根

我们可以举一些具体的例子,毕竟高度为h的树a,b,c毕竟是抽象的

当h == 0时,a,b,c树都为空,新增节点直接插入到60上,按我们之前分析的方式进行旋转

完全可以达到旋转的目的

当h == 1时,a,b,c树都为一个节点,新增节点直接插入到c树的右边或者左边都行,都会导致

parent的bf为2,cur的bf为1,按我们之前分析的方式进行旋转,也完全可以达到旋转的目的

 当h == 2时,情况就会复杂很多,毕竟三个节点,能够组成的情况本身就有3种

不过a,b,c子树并不是任意取的,有一定的限制,即c树必须为x的形状,假如不是x的形状,那往c

树这边插入新节点,是不会导致parent的bf变为2,cur的bf变为1这个右旋前提的

最简单的比如,假如c是y的形状,那新增一个右节点,甚至不需要旋转,直接插入即可 

如果新增一个左节点,此时就要左旋或者左右双旋

而a,b子树可以为x,y,z中的任意一种,总共3*3*1 *4 (4个插入位置)= 36种情况

这里简单列举全部都为x形状的情况

可以看到倒V形仍然适用 

有了思路,代码实现起来就很轻松,按部就班链接节点即可

不过,还有几点需要注意

第一,链接的同时,记得节点的_parent也要调整

第二,subRL可能为空,所以在链接其父亲的时候,要先判别其是否为空,防止空指针解引用

第三,调整时未必是整棵树调整,所以还需要考虑链接会大树的问题

 

第四,平衡因子记得也要调整 

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

		//b变成30的右
		parent->_right = subRL;
		//父节点也需要调整,但subRL可能为空
		if (subRL)
			subRL->_parent = parent;

		//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
		Node* ppNode = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

		if (ppNode == nullptr)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{   
			//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
			//subR重新链接回爷爷的左或者右
			if (ppNode->_right == parent)
			{
				ppNode->_right = subR;
			}
			else
			{
				ppNode->_left = subR;
			}

			subR->_parent = ppNode;
		}

		//调整平衡因子
		parent->_bf = subR->_bf = 0;
	}

2.4 右单旋

右旋和左旋类似,不过采取的是另外一种左高右低的模型

 不过虽然模型不同,但旋转调整的目的依旧不变

让这棵子树重新平衡,同时降低这棵子树的高度

因此右旋的结果,依旧和左旋保持一致,将中间节点提上去,保证它依旧是搜索二叉树下,形成一

个倒V模型

右单旋的具体场景也和左单旋非常类似

 

 

若把60称作parent节点,30称作为subL节点,很容易发现,当往a这棵子树新插入节点的时候,

parent对应的bf就由原来的-1变为-2,subL(cur)就由原来的0变为-1,符合我们之前所定义的需要右

旋的情况

 

那具体如何旋转呢?就是按照倒v模型

1.b变成60的左边

2.60变成30的右边

30变成整棵树的根

同样的,左旋需要注意的点,右旋也同样要注意

第一,链接的同时,记得节点的_parent也要调整

第二,subRL可能为空,所以在链接其父亲的时候,要先判别其是否为空,防止空指针解引用

第三,调整时未必是整棵树调整,所以还需要考虑链接会大树的问题

第四,平衡因子记得也要调整 

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

		//b变成60的左
		parent->_left = subLR;
		//父节点也需要调整,但subRL可能为空
		if (subLR)
			subLR->_parent = parent;

		//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
		Node* ppNode = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (ppNode == nullptr)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
			//subL重新链接回爷爷的左或者右
			if (ppNode->_right == parent)
			{
				ppNode->_right = subL;
			}
			else
			{
				ppNode->_left = subL;
			}

			subL->_parent = ppNode;
		}

		//调整平衡因子
		parent->_bf = subL->_bf = 0;
	}

2.5 LR双旋

之所以会出现双旋概念,就是因为单纯的单旋无法解决某类模型

假如对这类模型进行左旋,充其量也仅仅是做一个镜像对称,没有解决任何问题

 

但上面得到的模型,显然没有满足我们旋转的目的

所以我们才需要采用双旋来解决

先对下面的两个节点来个左旋,变成我们最熟悉的模型,再对其进行右旋

即可变成我们想要的倒V模型,让这棵子树重新平衡,同时降低这棵子树的高度

 

具体的模型如下

 

若把60称作parent节点,30称作为subL节点,很容易发现,当往b,c这两棵子树任意位置新插入节

点的时候,parent对应的bf就由原来的-1变为-2,subL(cur)就由原来的0变为1,符合我们之前所定

义的需要LR双旋的情况

由于前面已经实现过左单旋,和右单旋,所以双旋直接赋用即可

先对30来个左旋

再对90来个右旋

我们遮住其中的变化过程,只看首尾变化的话,也可以进一步理解LR双旋具体如何操作

subLR当根,然后把它的左子树分给subL,将它的右子树分给parent,保证了它仍然是一棵搜索二叉树,并达到高度平衡 

不过虽然完成了调整,但双旋真正的难点在于平衡因子的调整,单纯的单旋,平衡因子都会被调整

为0,但是实际最后的平衡因子,按照图片也知道并不是0

往b插入新节点,和往c插入新节点,最后平衡因子的变化是不同的;h==0时,插入新节点,平衡

因子变化也是不同的,需要分类讨论

当往b插入新节点,b树的高度就为h,则subL的bf就为0,与之相对,parent的bf就要变为1

subLR的bf始终为0

当往c插入新节点,c树的高度就为h,则subL的bf就为-1,与之相对,parent的bf就要变为0

subLR的bf始终为0

当h == 0时,不需要调整,三个节点的bf都为0

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

		//记录subLR初始的bf值,后续平衡因子调整需要使用
		int bf = subLR->_bf;
        //先对30来个左旋,再对90来个右旋
		//RotateL(subL);
		RotateL(parent->_left);
		RotateR(parent);

		//平衡因子调整有三种情况,一是b新增,二是c新增,三是h == 0的情况
		//c新增
		if (bf == 1)
		{
			parent->_bf = 0;
			subLR->_bf = 0;
			subL->_bf = -1;
		}
		//b新增
		else if (bf == -1)
		{
			parent->_bf = 1;
			subLR->_bf = 0;
			subL->_bf = 0;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			subLR->_bf = 0;
			subL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

2.6 RL双旋

RL双旋和LR双旋就是类似的操作了

它的提出也是因为单选无法解决这类模型,而且的的确确存在这种情况

对它直接进行右旋,只会得到一个镜像模型,当然可以先右旋,再LR双旋,理论上也是可以的

 

类似的,先对下面的两个节点来个右旋,变成我们最熟悉的模型,再对其进行左旋

即可变成我们想要的倒V模型,让这棵子树重新平衡,同时降低这棵子树的高度

具体的模型如下

 

类似操作,先对90左旋

  

再对30右旋

 

我们遮住其中的变化过程,只看首尾变化的话,也可以进一步理解RL双旋具体如何操作

subRL当根,然后把它的左子树分给parent,将它的右子树分给subR,保证了它仍然是一棵搜索二叉树,并达到高度平衡 

   

同样的,RL双旋的平衡因子都会被调整,依旧要对它进行分类讨论调整

当往b插入新节点,b树的高度就为h,则parent的bf就为0,与之相对,subR的bf就要变为1

subLR的bf始终为0

当往c插入新节点,c树的高度就为h,则subR的bf就为0,与之相对,parent的bf就要变为-1

subLR的bf始终为0

当h == 0时,不需要调整,三个节点的bf都为0


	//右左双旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		//记录subLR初始的bf值,后续平衡因子调整需要使用
		int bf = subRL->_bf;
		//先对30来个左旋,再对90来个右旋
		RotateR(parent->_right);
		RotateL(parent);

		//平衡因子调整有三种情况,一是b新增,二是c新增,三是h == 0的情况
		//c新增
		if (bf == 1)
		{
			parent->_bf = -1;
			subRL->_bf = 0;
			subR->_bf = 0;
		}
		//b新增
		else if (bf == -1)
		{
			parent->_bf = 0;
			subRL->_bf = 0;
			subR->_bf = 1;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			subRL->_bf = 0;
			subR->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

2.7 AVLTree树验证

验证一棵二叉树为AVL树,有三个方面需要验证

1.高度差是否正确

2.每个节点的平衡因子是否正确

3.是否为一棵二叉搜索树,中序遍历是否有序

template <class K,class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
//每个节点中还存一个指向父节点的指针
AVLTreeNode<K, V>* _parent;
pair<K, V> _kv;
int _bf; //balance factor

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

template <class K, class V>
class AVLTree
{   
typedef AVLTreeNode<K, V> Node;
public:
//插入
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 = new Node(kv);
	//将节点链接到AVLTree上
	if (parent->_kv.first > cur->_kv.first)
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}
	cur->_parent = parent;

	//更新因子
	while (parent)
	{
		//插入新增节点,会影响祖先的平衡因子
		//首先父亲节点的平衡因子肯定会改变
		if (cur == parent->_right)
		{
			parent->_bf++;
		}
		else
		{
			parent->_bf--;
		}

		//考虑是否要向上更新的问题
		//而是否要向上更新,按照AVLTree的规则就是看爷爷节点的bf是否被破坏
		//假如没有变了,则需要更新;没变,则不需要更新
		if (parent->_bf == 1 || parent->_bf == -1)
		{   
			//继续向上更新
			parent = parent->_parent;
			cur = cur->_parent;
		}
		else if (parent->_bf == 0)
		{
			break;
		}
		else if(parent->_bf == 2 || parent->_bf == -2)
		{
			// 需要旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度
			if (parent->_bf == 2 && cur->_bf == 1)
			{
				RotateL(parent);
			}
			else 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)
			{
				RotateRL(parent);
			}
			else
			{
				assert(false);
			}

			break;
		}
		else
		{
			assert(false);
		}
	}
	return true;
}
//中序遍历
void Inorder()
{
	_Inorder(_root);
	cout << endl;
}
//验证AVLTree是否建成功
bool IsBalance()
{
	return _IsBalance(_root);
}
//求树的高度
int Height()
{
	return _Height(_root);
}
private:
int _Height(Node* root)
{
	if (root == NULL)
		return 0;
	int leftH = _Height(root->_left);
	int rightH = _Height(root->_right);
	return leftH > rightH ? leftH + 1 : rightH + 1;
}
//判断右子树和左子树高度差绝对值是否小于1
bool _IsBalance(Node* root)
{
	if (root == nullptr)
		return true;

	int leftH = _Height(root->_left);
	int rightH = _Height(root->_right);

	//平衡因子是否更新正确
	if (rightH - leftH != root->_bf)
	{
		cout << root->_kv.first << "节点平衡因子异常" << endl;
		return false;

	}
	//从下往上,每一棵子树都是一棵平衡二叉树,则就是一棵平衡二叉树
	return abs(rightH - leftH) < 2
		&& _IsBalance(root->_left)
		&& _IsBalance(root->_right);
}
//左旋
void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

	//b变成30的右
	parent->_right = subRL;
	//父节点也需要调整,但subRL可能为空
	if (subRL)
		subRL->_parent = parent;

	//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
	Node* ppNode = parent->_parent;

	subR->_left = parent;
	parent->_parent = subR;

	if (ppNode == nullptr)
	{
		_root = subR;
		_root->_parent = nullptr;
	}
	else
	{   
		//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
		//subR重新链接回爷爷的左或者右
		if (ppNode->_right == parent)
		{
			ppNode->_right = subR;
		}
		else
		{
			ppNode->_left = subR;
		}

		subR->_parent = ppNode;
	}

	//调整平衡因子
	parent->_bf = subR->_bf = 0;
}

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

	//b变成60的左
	parent->_left = subLR;
	//父节点也需要调整,但subRL可能为空
	if (subLR)
		subLR->_parent = parent;

	//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
	Node* ppNode = parent->_parent;

	subL->_right = parent;
	parent->_parent = subL;

	if (ppNode == nullptr)
	{
		_root = subL;
		_root->_parent = nullptr;
	}
	else
	{
		//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
		//subL重新链接回爷爷的左或者右
		if (ppNode->_right == parent)
		{
			ppNode->_right = subL;
		}
		else
		{
			ppNode->_left = subL;
		}

		subL->_parent = ppNode;
	}

	//调整平衡因子
	parent->_bf = subL->_bf = 0;
}

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

	//记录subLR初始的bf值,后续平衡因子调整需要使用
	int bf = subLR->_bf;
    //先对30来个左旋,再对90来个右旋
	//RotateL(subL);
	RotateL(parent->_left);
	RotateR(parent);

	//平衡因子调整有三种情况,一是b新增,二是c新增,三是h == 0的情况
	//c新增
	if (bf == 1)
	{
		parent->_bf = 0;
		subLR->_bf = 0;
		subL->_bf = -1;
	}
	//b新增
	else if (bf == -1)
	{
		parent->_bf = 1;
		subLR->_bf = 0;
		subL->_bf = 0;
	}
	else if (bf == 0)
	{
		parent->_bf = 0;
		subLR->_bf = 0;
		subL->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

//右左双旋
void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

	//记录subLR初始的bf值,后续平衡因子调整需要使用
	int bf = subRL->_bf;
	//先对30来个左旋,再对90来个右旋
	RotateR(parent->_right);
	RotateL(parent);

	//平衡因子调整有三种情况,一是b新增,二是c新增,三是h == 0的情况
	//c新增
	if (bf == 1)
	{
		parent->_bf = -1;
		subRL->_bf = 0;
		subR->_bf = 0;
	}
	//b新增
	else if (bf == -1)
	{
		parent->_bf = 0;
		subRL->_bf = 0;
		subR->_bf = 1;
	}
	else if (bf == 0)
	{
		parent->_bf = 0;
		subRL->_bf = 0;
		subR->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

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

	_Inorder(root->_left);
	cout << root->_kv.first << " ";
	_Inorder(root->_right);
}
private:
Node* _root = nullptr;
};

 分别用两组测试代码进行测试

第一组为一固定的具体值,首先通过它来看代码整体是否有误

void Test_AVLTree1()
{
	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	AVLTree<int, int> t1;
	for (auto e : a)
	{
		/*	if (e == 14)
			{
			int x = 0;
			}*/

		t1.Insert(make_pair(e, e));
		cout << e << "插入:" << t1.IsBalance() << endl;
	}

	t1.Inorder();
	cout << t1.IsBalance() << endl;
}

对应结果

  

第二组为多组随机值,如果能够全部每次通过,则代码整体没有问题

void Test_AVLTree2()
{
	srand(time(0));
	const size_t N = 100000;
	AVLTree<int, int> t;
	for (size_t i = 0; i < N; ++i)
	{
		size_t x = rand() + i;
		t.Insert(make_pair(x, x));
		//cout << t.IsBalance() << endl;
	}

	//t.Inorder();

	cout << t.IsBalance() << endl;
	//cout << t.Height() << endl;
}

对应结果,由于数据过大,所以这里没有采取中序遍历全部打印

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值