【AVL树】

AVL树

小杨

AVL树的概念

二叉搜索树可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
因此引入了一种解决办法:当向二叉搜索树中插入新节点后,如果能保证每个节点的左右子树高度之差的绝对值不超过1(如果超过,就需要对树中的节点进行调整),即可降低树的高度,从而减少平均搜索长度。
AVL树具有的性质:

  1. AVL树的左右子树也是AVL树
  2. 左右子树高度之差(平衡因子)的绝对值不超过1(-1/0/1)
    在这里插入图片描述
    如果一颗二叉搜索树是高度平衡的,那么他就是AVL树,如果它有n个节点,其高度可保持在0(logN),搜索时间复杂度0(logN).(这里的对数我们默认都是以2为底的)。

AVL树节点的定义

AVL树节点的定义.

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

AVL的构造析构相关

这里的拷贝构造有节点的开辟,需要空间,因此需要用深拷贝。这里直接复用Copy函数(递归来进行)来进行深拷贝。
赋值拷贝构造直接利用现代写法,用传值传参调用拷贝构造生成的临时对象,然后与进行交换。
析构函数,递归来进行一个一个的delete。
因为写了拷贝构造,那么编译器就不会再生成默认的构造了,这里我们就利用default,让其来强行的生成默认的拷贝构造。

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;

public:
	AVLTree() = default;

	AVLTree(const AVLTree<K, V>& t)
	{
		_root = Copy(t._root);
	}

	AVLTree<K, V>& operator = (AVLTree<K, V> t)
	{
		swap(_root, t._root);
		return *this;
	}

	~AVLTree()
	{
		Destroy(_root);
		_root = nullptr;
	}
private:
	void Destroy(Node* root)
	{
		if (root == nullptr)
			return;
		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
	}
	
	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;
	
		Node* newRoot = new Node(root->_key, root->_value);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);
	
		return newRoot;
	}
private:
	Node* _root = nullptr;
};

AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。AVL树的插入过程可以分为两步:

  1. 按照二叉搜索树的方式插入新节点。
  2. 调整节点中的平衡因子。

新插入节点后是需要分情况的,因为引入了平衡因子的概念,因此我们需要时刻保证平衡因子的稳定。
插入节点,会影响部分祖先节点的平衡因子。插入在左子树,平衡因子减减;插入在右子树,平衡因子加加;是否要继续往上更新祖先,是要看parent所在子树高度是否变化。

  1. parent平衡因子为0,因此parent所在子树高度不变,不需要向上更新。
  2. parent的平衡因子为1/-1,parent的平衡因子更新前为0;插入在任意一遍,parent所在子树高度变化,需要向上更新。
  3. parent的平衡因子为2/-2,说明parent的平衡因子是1/-1,插入节点插入在高的那一边,进一步加剧了parent所在子树的不平衡,已经违反规则了,需要旋转处理。
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
		{
			//拥有相同的值,返回false,插入失败
			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)
	{
		//看刚才插入的新节点是其的左还是右
		//如果是右,平衡因子就加1
		//如果是左,平衡因子就减1
		if (cur == parent->_left)
			parent->_bf--;
		else
			parent->_bf++;

		//因为插入前这个树是平衡的,如果平衡因子为0,就证明插入的这个节点,不影响AVL树的平衡,此时树也是平衡的,就不需要调整。
		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->_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)
			{
				RotateRL(parent);
			}
			else
			{
				RotateLR(parent);
			}

			break;
		}
		else
		{
			assert(false);
		}
	}

	return true;
}

AVL树的旋转

如果在一颗原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:

新节点插入较高右子树的右侧—右右:左单旋

对下面的图进行一系列分析:
在这里插入图片描述

  1. h==0
    在这里插入图片描述

  2. h==1
    在这里插入图片描述

  3. h==2
    h为2时AVL子树有3种情况:
    在这里插入图片描述
    在这里插入图片描述
    a和b可以是x、y、z中任意一种情况,但是c必须是x(有4种方式,都会不断向上更新),如果c为y/z的话,他不可能插入一个节点以后更新到祖先即图中30节点了。
    因此排列组合情况有,334=36.当然不论这36种是哪一种,都是把60的左变为30的右,30变成60的左。

  4. h==3
    AVL子树大体上有两种情况:
    在这里插入图片描述
    其中y情况是抽象的,其实y情况的种数为:C4选1+C4选2+C4选3+C4选4,一共为14种.
    因此总的来说,a和b是x/y中的任意一种:即1515,c是x插入位置:8,C是y中的4选3中(只能在4个节点中的3个节点中的两个节点相邻时的位置插入,也就是4种),即为:4C4选3=16;
    因此h为3时,此时情况一共有:(14+1)(14+1)(8+16)=6120种;
    在这里插入图片描述

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

	//将subR的左子树给与parent的右
	parent->_right = subRL;
	//如果subrl为真,那么就更新其的父亲。
	if (subRL)
		subRL->_parent = parent;

	//记录最初父节点的父亲。
	Node* parentParent = parent->_parent;
	//subr的左为parent
	subR->_left = parent;
	//更新parent的父亲节点
	parent->_parent = subR;

	//如果之前父亲节点的父亲为空,那么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 = 0;
	subR->_bf = 0;
}
新节点插入较高左子树的左侧—左左:右单旋

在这里插入图片描述

/*
  上图在插入前,AVL树是平衡的,新节点插入到30的左子树(注意:此处不是左孩子)中,30左
子树增加
  了一层,导致以60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子
树增加一层,
  即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有
右子树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新节点
的平衡因子即可。在旋转过程中,有以下几种情况需要考虑:
  1. 30节点的右孩子可能存在,也可能不存在
  2. 60可能是根节点,也可能是子树
     如果是根节点,旋转完成后,要更新根节点
     如果是子树,可能是某个节点的左子树,也可能是右子树
*/
void _RotateR(PNode pParent)
{
    // pSubL: pParent的左孩子
    // pSubLR: pParent左孩子的右孩子,注意:该
 PNode pSubL = pParent->_pLeft;
 PNode pSubLR = pSubL->_pRight;
    // 旋转完成之后,30的右孩子作为双亲的左孩子
 pParent->_pLeft = pSubLR;
    // 如果30的左孩子的右孩子存在,更新亲双亲
 if(pSubLR)
 pSubLR->_pParent = pParent;
    // 60 作为 30的右孩子
 pSubL->_pRight = pParent;
    
    // 因为60可能是棵子树,因此在更新其双亲前必须先保存60的双亲
 PNode pPParent = pParent->_pParent;
    
    // 更新60的双亲
 pParent->_pParent = pSubL;
    
    // 更新30的双亲
 pSubL->_pParent = pPParent;
    // 如果60是根节点,根新指向根节点的指针
 if(NULL == pPParent)
 {
 _pRoot = pSubL;
 pSubL->_pParent = NULL;
 }
 else
 {
   // 如果60是子树,可能是其双亲的左子树,也可能是右子树
 if(pPParent->_pLeft == pParent)
 pPParent->_pLeft = pSubL;
 else
 pPParent->_pRight = pSubL;
 }
    // 根据调整后的结构更新部分节点的平衡因子
 pParent->_bf = pSubL->_bf = 0;
}
新节点插入较高左子树的右侧—左右:先左单旋再右单旋

在这里插入图片描述
将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后考虑平衡因子的更新。

//左右双旋
void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	//需要记录subLR的平衡因子(需要这个来更新最终的平衡因子)
	// 旋转之前,保存pSubLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子
	int bf = subLR->_bf;

	// 先对30进行左单旋
	RotateL(parent->_left);
	// 先对60进行右单旋(60此时已经变成了90的左边)
	RotateR(parent);

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


}
新节点插入较高右子树的左侧—右左:先右单旋再左单旋

在这里插入图片描述

void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	//需要记录subLR的平衡因子(需要这个来更新最终的平衡因子)
	// 旋转之前,保存pSubLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子
	int bf = subRL->_bf;

	// 先对90进行右单旋
	RotateR(parent->_right);
	// 先对30进行右单旋(90此时已经变成了30的右边)
	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);
	}

}

总结:
假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑

  1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR
    当pSubR的平衡因子为1时,执行左单旋
    当pSubR的平衡因子为-1时,执行右左双旋
  2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL
    当pSubL的平衡因子为-1是,执行右单旋
    当pSubL的平衡因子为1时,执行左右双旋
    旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。

AVL树的查找

找到相等就返回。

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

AVL树的中序遍历

如果我们想要在函数中调用的话,会发现如果我们将_root设置为私有变量,即我们访问不了,那么我们是否将中序遍历封装一下,即在该类中由公有成员方法中序遍历来调用真正的在类中核心的需要复用的遍历方法,此时,那我们外面调用的时候就不用再传参了。

class AVLTree
{
public:
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
	
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}
}
//如何调用?
t.InOrder();

AVL树的节点数和高度

与中序遍历同理,都进行类内部复用,这样就不用传参了。

class AVLTree
{
public:
	int Size()
	{
		return _Size(_root);
	}
	
	int Height()
	{
		return _Height(_root);
	}
private:
	int _Size(Node* root)
	{
		return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
	}
	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;
	}
}

AVL树的验证

AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

  1. 验证其为二叉搜索树
    如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
  2. 验证其为平衡树
    每个节点子树高度差的绝对值不超过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);
}

AVL总代码及测试用例:

#pragma once
#pragma optimize( "g", on )
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
#include<vector>

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:
	AVLTree() = default;

	AVLTree(const AVLTree<K, V>& t)
	{
		_root = Copy(t._root);
	}

	AVLTree<K, V>& operator = (AVLTree<K, V> t)
	{
		swap(_root, t._root);
		return *this;
	}

	~AVLTree()
	{
		Destroy(_root);
		_root = nullptr;
	}
	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
			{
				//拥有相同的值,返回false,插入失败
				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)
		{
			//看刚才插入的新节点是其的左还是右
			//如果是右,平衡因子就加1
			//如果是左,平衡因子就减1
			if (cur == parent->_left)
				parent->_bf--;
			else
				parent->_bf++;

			//因为插入前这个树是平衡的,如果平衡因子为0,就证明插入的这个节点,不影响AVL树的平衡,此时树也是平衡的,就不需要调整。
			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->_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)
				{
					RotateRL(parent);
				}
				else
				{
					RotateLR(parent);
				}

				break;
			}
			else
			{
				assert(false);
			}
		}

		return true;
	}


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


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

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

	

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

	int Height()
	{
		return _Height(_root);
	}
private:
	int _Size(Node* root)
	{
		return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
	}
	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;
	}
	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);
	}

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

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

	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		//需要记录subLR的平衡因子(需要这个来更新最终的平衡因子)
		// 旋转之前,保存pSubLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子
		int bf = subRL->_bf;

		// 先对90进行右单旋
		RotateR(parent->_right);
		// 先对30进行右单旋(90此时已经变成了30的右边)
		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 RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		//需要记录subLR的平衡因子(需要这个来更新最终的平衡因子)
		// 旋转之前,保存pSubLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子
		int bf = subLR->_bf;

		// 先对30进行左单旋
		RotateL(parent->_left);
		// 先对90进行右单旋(60此时已经变成了90的左边)
		RotateR(parent);

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


	}

	void Destroy(Node* root)
	{
		if (root == nullptr)
			return;
		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
	}

	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;

		Node* newRoot = new Node(root->_key, root->_value);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);

		return newRoot;
	}

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

		//将subR的左子树给与parent的右
		parent->_right = subRL;
		//如果subrl为真,那么就更新其的父亲。
		if (subRL)
			subRL->_parent = parent;

		//记录最初父节点的父亲。
		Node* parentParent = parent->_parent;
		//subr的左为parent
		subR->_left = parent;
		//更新parent的父亲节点
		parent->_parent = subR;

		//如果之前父亲节点的父亲为空,那么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 = 0;
		subR->_bf = 0;
	}
	//右旋转
	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 == nullptr)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subL;

			}
			else
			{
				parentParent->_right = subL;
			}

			subL->_parent = parentParent;
		}
		//平衡因子全部归零
		parent->_bf = subL->_bf = 0;
	}
	//左旋转

private:
	Node* _root = nullptr;
};

void TestAVLTree()
{
	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 });

		cout << e << "->" << t.IsBalanceTree() << endl;
	}

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

void TestAVLTree2()
{
	const int N =10000000 ;
	vector<int> v;
	v.reserve(N);
	srand(time(0));

	for (size_t i = 0; i < N; ++i)
	{
		v.push_back(rand());
		//cout<<v.back()<<endl;
	}

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

void TestAVLTree3()
{

			vector<int> v{ 25059,14777,14692,14409,18330,9370,8178,7509,9114 };
			v.reserve(9);
			int j = 1;
			size_t begin2 = clock();
			AVLTree<int, int> t;
			
			for (int i = 0; i < v.size(); ++i)
			{
				if (i == v.size() - 1)
				{
					t.Insert(make_pair(v[i], v[i]));
				}
				cout << j++ << endl;
				cout << v[i] << endl;
				t.Insert(make_pair(v[i], v[i]));
			}
	
			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 < v.size(); ++i)
			{
				t.Find((rand() + i));
			}
	
			size_t end1 = clock();
	
			cout << "Find:" << end1 - begin1 << endl;
}


AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即log_2 (N)。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值