AVL树模拟实现

87 阅读19分钟

一.AVL树的基本概念

在我们实现过二叉搜索树后,我们知道尽管搜索树可以做到严格平衡,但是当数据有序或者接近有序的时候就会退化为单支树,此时用搜索树查找元素相当于在顺序表中查找数据,效率较低,达不到我们想要的效果,由此,针对搜索树出现了很多优化,我们主要介绍两种——AVL树和红黑树。

今天我们先介绍AVL树,AVL是由两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年发明的,主要内容是:为搜索树的每个节点加入平衡因子,当插入新节点时,同时调整平衡因子,使得子树的相对高度不超过1(平衡过程中需要调整子树),通过这种方法,在保持搜索树的性质的同时,降低树的高度,提高搜索效率。

AVL树,或者空的AVL,都是具有以下性质的二叉搜索树:

(1).它的左右子树都是AVL树。

(2).每个树的左右子树的高度之差为绝对值1或者0,其中每个数左右子树的高度差用平衡因子表示。

image-20230427140721913

由上的性质我们可以看出,AVL树是高度平衡的搜索树,如果它具有n个节点,它的高度可以保持在(log_2 n), 遍历的时间复杂度为(log_2 n)。

二.AVL树的实现

2.1 AVL树的节点

对于AVL树而言,每一个节点不再是只保存一个 数据了,为了方便遍历,我们还需要记录每颗树的左右子树和每颗树的父亲,同时还要包含我们的平衡因子,具体实现如下:

template<class K , class V>//使用模板,增加代码的可移植性
class  AVLTreeNode
{
public:
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	pair<K, V> _kv;//用键对值记录下搜索树比较的key和数据v
	int _bf;//平衡因子

	//节点自带的构造函数
	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0)
	{}
    
    //自定义类型要写析构
	~AVLTreeNode()
	{
		_left = _right = _parent = nullptr;
		_kv = _bf = 0;
		delete *this;
	}
};

2.2AVL树的大致框架

对我们来说,只需要了解AVL树的插入即可,这是AVL树比较有优势的地方,故我们主要实现插入这个接口,基本的框架如下:

template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;   
Node* _root;//根节点
 
public:
    //构造函数
    AVLTree()
        :_root(nullptr)
        {}
};

大家可以思考下,我们实现了节点的析构函数,还需要写AVLTree的析构函数吗?

其实不用的,因为根节点也属于节点,最后会自动调用节点的析构函数释放根节点。

2.3AVL树的插入

铺垫了这么多,终于进入我们的重头戏了,实现AVL树的插入。AVL树的插入大致分为3部分——按照搜索树的规则插入,调节平衡因子,旋转,接下来我们一一介绍。

2.3.1按照搜索树的规则插入

实现过搜索树的朋友,这部分应该很轻松。对于插入,第一次插入,即空树的插入,让该节点成为根节点,第n次插入,迭代,直到找到合适的位置,插入进去,具体如下:

bool AVLTreeInsert(const pair<K, V>& kv)
{
		if (_root == nullptr)//当树为空时
		{
			_root = new Node(kv);
			return true;
		}

		Node* cur = _root;
		Node* parent = nullptr;
		
		while (cur)//当树不为空时,迭代
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;//key相同,不允许插入
			}
		}

		cur = new Node(kv);//走到了空位置开始插入
		if (parent->_kv.first < kv.first)//连接到父亲的左树或者右树
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else//不可能再相等了,可以不判断
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
    
    //调节平衡因子
}

2.3.2 调节平衡因子

当节点插入时,我们默认节点的平衡因子为0,即无子树或者左右子树平衡,左子树插入,父亲的平衡因子减1,表示左树变高,右子树插入时,父亲的平衡因子加1,表示右树变高。

这样衍生出两个问题:

(1)何时达到重新平衡?

(2)当持续不断插入时,会不会影响到子树上方的部分,需不需要调整?

image-20230427152858847

由上图可以发现,当插入21时,父亲30的平衡因子为0,对于30这个树而言平衡了,此时这部分对于整个树来说没有任何影响,因此我们不需要再调整。所以,当父亲的平衡因子为0时,则不需要再向上调整。

image-20230427153146496

由上图可以看出,在不断的插入中,是会持续影响到上方的节点,同时会导致上方节点的严重不平衡。另外,我们发现,当父节点的平衡因子为1或者-1说明不平衡,同时从整体来看,此时的左右子树并没有出现实际的高度差,我们通过调节平衡因子是可以达到平衡的。但是,像5这样的节点插入时,整个树的高度明显高了,7这个节点的平衡因子为-2,此时再单纯用调节平衡因子的方法,是不能达到平衡的,因为整个树的高度差是仍然存在的。

由上两图我们可以得出我们调整的基本规律:

插入新节点的时候,当父节点的平衡因子为1或者-1时,持续向上调整,直至父节点的平衡因子为0,当父节点的平衡因子为2或者-2,则需要旋转。

在实现调节平衡因子之前,我希望各位朋友可以思考一个问题,5在插入时,6的平衡因子变成了-1,向上调整的过程中,为什么4的平衡因子变成了1而不是-1,7的平衡因子为什么又变成-2?

具体调节可以参考下方代码:

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

		Node* cur = _root;
		Node* parent = nullptr;
		
		while (cur)//当树不为空时,迭代
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;//key相同,不允许插入
			}
		}

		cur = new Node(kv);//走到了空位置开始插入
		if (parent->_kv.first < kv.first)//连接到父亲的左树或者右树
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else//不可能再相等了,可以不判断
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
    
   
		//每次插入都要控制平衡,更新平衡因子,路径是节点到根节点的祖先路径,出现异常因子的时候,需要旋转平衡处理
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;//左边就减1
			}
			else if (cur == parent->_right)
			{
				parent->_bf++;//右边就加1
			}

			//当因子等于0就平衡了,不用再调节
			if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				//说明还是有一边的子树高了,就要继续往上调整,1和-1不表示左右子树已经出现高度差,只是有了这样的倾向,所以要接着向上调整
				cur = parent;//往上迭代
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				//树已经不平衡了,需要旋转调整		
			}
			else
			{
				assert(false);//走到这里说明在更新平衡因子之前树就已经不平衡,整体出错
			}
		}
		return true;//插入成功
	}

2.3.3旋转调整

2.3.3.1单旋

image-20230427161921019

我们观察上图,当节点插入在60的左子树,90的平衡因子为-2,说明需要旋转,此时整个树偏左,则需要向右旋转,降低高度。

image-20230427163540037

如上图,就是我们右旋的整个过程,有两个要注意的点:

(1)parent可能为根节点,也可能上方还有节点,因此要考虑subL和Gparent的链接关系。

(2)subLR可能存在,也可能不存在,parent也要注意和subLR连接

具体实现如下(右旋同理,不再赘述):

void RotateR(Node* parent)//不需要引用,因为是指针
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		//把原来parent的左树的右树连接到parent的左边
		parent->_left = subLR;//parent是一定要和subLR链接的,但是subLR为空的话就不用再更新subLR的parent,而不是因为subLR不为空,才进行双向链接,不为空是双向链接,为空是单向连接链接
		if (subLR)
		{
			subLR->_parent = parent;
		}


		Node* GParent = parent->_parent;//parent可能不是根节点,所以要把它上方的节点给连接过去

		//链接
		subL->_right = parent;
		parent->_parent = subL;

		if (parent == _root)//可能一开始的parent是根节点,就不用再继承了
		{
			//parent是根节点的话,旋转过后就让subL当根节点,其父亲指向空
			_root = subL;//这两句话不要改变顺序,parent是根,才让subL为根,然后让subL的parent置为空,反过来意思就不一样了
			//_root->_parent = nullptr;
			subL->_parent = nullptr;
		}
		else//不是空的话就连接过去
		{
			if (GParent->_left == parent)
			{
				GParent->_left = subL;
			}
			else
			{
				GParent->_right = subL;
			}

			subL->_parent = GParent;//parent不是根节点,subL就继承
		}

		//最后更正平衡因子
		//要注意,能影响平衡因子的只有节点的子树,因此在单旋中只影响了parent和subL的因子,subLR的没有影响
		subL->_bf = parent->_bf = 0;
	}
2.3.3.2双旋

image-20230427204035581

上图就是我们左右双旋的过程,第一次旋转让我们的树整体偏左,再右旋就可以降低高度。建议各位朋友实现的时候,画好图,同时观察上图,衍生出两个问题:

(1)怎么判断要进行双旋?

(2)双旋以后还需要做别的处理吗?这部分树真的已经平衡了吗?还需要往上迭代调整吗?

要回答这两个问题,我们先回答2.3.2调节平衡因子中最后的那个问题,即5在插入时,6的平衡因子变成了-1,向上调整的过程中,为什么4的平衡因子变成了1而不是-1,7的平衡因子为什么又变成-2?

平衡因子代表了当前节点子树的高度差,同时-1和1也表示了倾向,-1表示左子树高度变高,1表示右子树高度变高。对6而言,是左子树高了,故标记为-1,对4而言是它的右子树变高了,故为1,对于7而言,是他的左子树整体变高,故为-2,10同理。搞明白这个问题,我们再来解决双旋的条件,请看下图:

image-20230427205130069

由上图我们可以发现,同样是在60的子树中插入,左边插入30时,整体偏左,60,90的平衡因子分别为-1,-2,均为负数,偏左,需要左旋;b或者c子树插入时,60, 90的平衡因子分别为1, -2,为折现,90偏左,60偏右,需要左右旋(这里的偏左偏右指的是树的下方或更偏向哪个方向)。左旋和右左旋同理,故旋转的条件可以总结为下:

//(1)  parent->_bf = -2 && cur->_bf == -1    右单旋
//(2)  parent->_bf = 2 && cur->_bf == 1      左单旋
//(3)  parent->_bf = -2 && cur->_bf == 1     先左旋再右旋 
//(4)  parent->_bf = 2 && cur->_bf ==- 1     先右旋再左旋

再思考下第二个问题:双旋以后还需要做别的处理吗?这部分树真的已经平衡了吗?还需要往上迭代调整吗?

依旧拿上图为例子,我们看看左单旋和左右双旋后整体的情况:

image-20230427205704034

右旋后,这部分数完全平衡了,而在左右旋后,60的右子树不平衡,90平衡了,30左子树不平衡。所以对于双旋,还需要调整平衡因子,对于单旋就可以都置为0,具体实现如下:

void RotateR(Node* parent)//不需要引用,因为是指针
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		//把原来parent的左树的右树连接到parent的左边
		parent->_left = subLR;//parent是一定要和subLR链接的,但是subLR为空的话就不用再更新parent,而不是因为subLR不为空,才进行双链接,不为空是双链接,为空是单链接
		if (subLR)
		{
			subLR->_parent = parent;
		}


		Node* GParent = parent->_parent;//parent可能不是根节点,所以要把它上方的节点给连接过去

		//链接
		subL->_right = parent;
		parent->_parent = subL;

		if (parent == _root)//可能一开始的parent是根节点,就不用再继承了
		{
			//parent是根节点的话,旋转过后就让subL当根节点,其父亲指向空
			_root = subL;//这两句话不要改变顺序,parent是根,才让subL为根,然后让subL的parent置为空,反过来意思就不一样了
			//_root->_parent = nullptr;
			subL->_parent = nullptr;
		}
		else//不是空的话就连接过去
		{
			if (GParent->_left == parent)
			{
				GParent->_left = subL;
			}
			else
			{
				GParent->_right = subL;
			}

			subL->_parent = GParent;//parent不是根节点,subL就继承
		}

		//最后更正平衡因子
		//subL->_bf = parent->_bf = subLR->_bf = 0;
	//要注意,能影响平衡因子的只有节点的子树,因此在单旋中只影响了parent和subL的因子,subLR的没有影响
		subL->_bf = parent->_bf = 0;
	}

	//左单旋和右原理一致,
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		//先连接subRL和parent
		parent->_right = subRL;
		if (subRL)
		{
			subRL->_parent = parent;
		}

		Node* GParent = parent->_parent;

		//链接subR和parent
		subR->_left = parent;
		parent->_parent = subR;

		//链接subR和原来parent的父节点
		
		if (_root == parent)
		{
			_root = subR;//parent为根节点,就比较简单,直接置为空,subR成为根节点
			subR->_parent = nullptr;
		}
		else
		{
			if (GParent->_left == parent)//别老是写错==啊
				GParent->_left = subR;
			else
				GParent->_right = subR;

			subR->_parent = GParent;//注意链接关系是双向的,两边都要链接才行
		}
		
		subR->_bf = parent->_bf = 0;
	}

	
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

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

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

	//左右旋,也是要考虑平衡因子的
	void RotateLR(Node* parent)
	{
		///*Node* subL = parent->_left;
		//Node* subLR = subL->_right;
		//int bf = subLR->_bf;*/
		
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

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

		if (bf == -1)
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			subL->_bf = -1;
			subLR->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			cout << "某处平衡因子已错误" << endl;
			assert(false);
		}
	}

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

		Node* cur = _root;
		Node* parent = nullptr;
		//当树不为空时,迭代
		while (cur)
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				//key相同,不允许插入
				return false;
			}
		}

		cur = new Node(kv);//走到了空位置开始插入
		if (parent->_kv.first < kv.first)//连接到父亲的左树或者右树
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else//不可能再相等了,所以倒也可以不用判断那么仔细
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		//每次插入都要控制平衡,更新平衡因子,路径是节点到根节点的祖先路径,出现异常因子的时候,需要旋转平衡处理
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;//左边就减1
			}
			else if (cur == parent->_right)
			{
				parent->_bf++;//右边就加1
			}

			//当因子等于0就平衡了,不用再调节
			if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				//说明还是有一边的子树高了,就要继续往上调整,1或者-1并不影响,但是需要调整因子
				cur = parent;//往上迭代
				parent = parent->_parent;//_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))
				{
					RotateL(parent);//左单旋
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				 
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				else
				{
					assert(false);
				}
				break;//旋转成功
			}
			else
			{
				//走到这里说明在更新平衡因子之前树就已经不平衡了
				assert(false);
			}
		}
		return true;//插入成功
	}

三.AVL树的验证

自己实现完AVL树,我们得想想怎么验证,这里不能用平衡因子来验证,因为平衡因子本来就是我们自己设计,用自己设计的来验证自己的AVL树,就有点监守自盗的意思了。

那如何验证呢?

我们可以验证高度,遍历整棵树,看看树的子树高度差的绝对值是不是1或者0。但是,只靠高度行吗?大概率是不行的,因为插入的过程中可能高度符合,但是平衡因子不符合,所以我们再验证下每个节点的平衡因子是否正确,双重验证下就可以保证AVL树的正确性,具体如下:

//检查是否平衡
	int Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		int RightHeight = Height(root->_left);
		int LeftHeight = Height(root->_right);

		return RightHeight > LeftHeight ? RightHeight + 1 : LeftHeight + 1;
	}

	bool IsBalance()
	{
		//不用判断是否是空指针
		//assert(root);
		return _IsBalance(_root);//父函数不能带参数,不然调不了

	}
	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;//空的话应该返回true,因为不影响平衡

		int LeftHeight = Height(root->_left);//迭代计算高度
		int RightHeight = Height(root->_right);

		if (RightHeight - LeftHeight != root->_bf)//仅仅判断高度是不够的,有可能平衡因子还是错了,所以要对每个平衡因子做检查
		{
			cout << "平衡因子现在是:" << root->_bf << endl;
			cout << "平衡因子应该是:" << (RightHeight - LeftHeight) << endl;
			return false;//平衡因子错了直接返回
		}

		return RightHeight - LeftHeight < 2 && _IsBalance(root->_left) && _IsBalance(root->_right);


	}

image-20230427211501926

具体源码如下:

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

template<class K , class V>
class  AVLTreeNode
{
public:
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	pair<K, V> _kv;
	int _bf;//平衡因子

	//节点自带的构造函数
	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0)
	{}

	//自定义类型要写析构
	~AVLTreeNode()
	{
		_left = _right = _parent = nullptr;
		_kv = _bf = 0;
		delete *this;
	}
};

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;//这里要加上模板参数,不然就不是我们想要的
	Node* _root = nullptr;
public:

	AVLTree()//构造
		:_root(nullptr)
	{}
	
	//根节点不用再写析构了,因为都属于节点
	/*~AVLTree()
	{
		_root->_left = _root->_right = nullptr;
		_root->_bf = _root->_kv = 0;
		delete _root;
		_root = nullptr;
	}*/
	void RotateR(Node* parent)//不需要引用,因为是指针
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		//把原来parent的左树的右树连接到parent的左边
		parent->_left = subLR;//parent是一定要和subLR链接的,但是subLR为空的话就不用再更新parent,而不是因为subLR不为空,才进行双链接,不为空是双链接,为空是单链接
		if (subLR)
		{
			subLR->_parent = parent;
		}

		Node* GParent = parent->_parent;//parent可能不是根节点,所以要把它上方的节点给连接过去

		//链接
		subL->_right = parent;
		parent->_parent = subL;

		if (parent == _root)//可能一开始的parent是根节点,就不用再继承了
		{
			//parent是根节点的话,旋转过后就让subL当根节点,其父亲指向空
			_root = subL;//这两句话不要改变顺序,parent是根,才让subL为根,然后让subL的parent置为空,反过来意思就不一样了
			//_root->_parent = nullptr;
			subL->_parent = nullptr;
		}
		else//不是空的话就连接过去
		{
			if (GParent->_left == parent)
			{
				GParent->_left = subL;
			}
			else
			{
				GParent->_right = subL;
			}

			subL->_parent = GParent;//parent不是根节点,subL就继承
		}

		//最后更正平衡因子
		//subL->_bf = parent->_bf = subLR->_bf = 0;
	//要注意,能影响平衡因子的只有节点的子树,因此在单旋中只影响了parent和subL的因子,subLR的没有影响
		subL->_bf = parent->_bf = 0;
	}

	//左单旋和右原理一致,
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		//先连接subRL和parent
		parent->_right = subRL;
		if (subRL)
		{
			subRL->_parent = parent;
		}

		Node* GParent = parent->_parent;

		//链接subR和parent
		subR->_left = parent;
		parent->_parent = subR;

		//链接subR和原来parent的父节点
		
		if (_root == parent)
		{
			_root = subR;//parent为根节点,就比较简单,直接置为空,subR成为根节点
			subR->_parent = nullptr;
		}
		else
		{
			if (GParent->_left == parent)//别老是写错==啊
				GParent->_left = subR;
			else
				GParent->_right = subR;

			subR->_parent = GParent;//注意链接关系是双向的,两边都要链接才行
		}
		
		subR->_bf = parent->_bf = 0;
	}

	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

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

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

	//左右旋,也是要考虑平衡因子的
	void RotateLR(Node* parent)
	{
		///*Node* subL = parent->_left;
		//Node* subLR = subL->_right;
		//int bf = subLR->_bf;*/
		
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

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

		if (bf == -1)
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			subL->_bf = -1;
			subLR->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			cout << "某处平衡因子已错误" << endl;
			assert(false);
		}
	}

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

		Node* cur = _root;
		Node* parent = nullptr;
		//当树不为空时,迭代
		while (cur)
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				//key相同,不允许插入
				return false;
			}
		}

		cur = new Node(kv);//走到了空位置开始插入
		if (parent->_kv.first < kv.first)//连接到父亲的左树或者右树
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else//不可能再相等了,所以倒也可以不用判断那么仔细
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		//每次插入都要控制平衡,更新平衡因子,路径是节点到根节点的祖先路径,出现异常因子的时候,需要旋转平衡处理
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;//左边就减1
			}
			else if (cur == parent->_right)
			{
				parent->_bf++;//右边就加1
			}

			//当因子等于0就平衡了,不用再调节
			if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				//说明还是有一边的子树高了,就要继续往上调整,1或者-1并不影响,但是需要调整因子
				cur = parent;//往上迭代
				parent = parent->_parent;//_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))
				{
					RotateL(parent);//左单旋
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				 
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				else
				{
					assert(false);
				}
				break;//旋转成功
			}
			else
			{
				//走到这里说明在更新平衡因子之前树就已经不平衡了
				assert(false);
			}
		}
		return true;//插入成功
	}

	void InOrder()
	{
		_InOrder(_root);
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)//注意这里是root==nullptr,不是_root,_root在递归中是不变的
			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 RightHeight = Height(root->_left);
		int LeftHeight = Height(root->_right);

		return RightHeight > LeftHeight ? RightHeight + 1 : LeftHeight + 1;
	}

	bool IsBalance()
	{
		//不用判断是否是空指针
		//assert(root);
		return _IsBalance(_root);//父函数不能带参数,不然调不了

	}
	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;//空的话应该返回true,因为不影响平衡

		int LeftHeight = Height(root->_left);//迭代计算高度
		int RightHeight = Height(root->_right);

		if (RightHeight - LeftHeight != root->_bf)//仅仅判断高度是不够的,有可能平衡因子还是错了,所以要对每个平衡因子做检查
		{
			cout << "平衡因子现在是:" << root->_bf << endl;
			cout << "平衡因子应该是:" << (RightHeight - LeftHeight) << endl;
			return false;//平衡因子错了直接返回
		}

		return RightHeight - LeftHeight < 2 && _IsBalance(root->_left) && _IsBalance(root->_right);


	}

};

void TestAVLTree()
{
	AVLTree<int, int> t;
	//int a[] = { 5 , 4, 3, 2, 1};
	//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	int a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
	for (auto e : a)
	{
		t.Insert(make_pair(e, e));
		cout << "Insert" << e << ":" << t.IsBalance() << endl;
	}

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