AVL树-->增加节点

1.概念

AVL树称为高度平衡的二叉搜索树,首先要满足二叉搜索树的性质。然后做了一定改进,能够使二叉搜索树及其子树的左右分支的分支高度平衡。这样能够大大的提高查找效率。对于AVL树,如果它有N个结点,其高度可保持在 log 2 N,搜索时间复杂度O(log 2 N )。

具体实现思路为:对每个结点都有一个平衡因子参数(一般为右树高度减去左树高度),当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

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

  1. 它的左右子树都是AVL树。
  2. 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)。

如下图:
(a),(b)都是二叉搜索树,但是(a)不是AVL树,(b)为AVL树。
在这里插入图片描述

AVL树节点的定义:

template<class Type>
class AVLNode
{
public:
	AVLNode() :data(Type()), leftChild(nullptr), rightChild(nullptr), bf(0)
	{}
	AVLNode(Type d, AVLNode* left = nullptr, AVLNode* right = nullptr)
		:data(d), leftChild(left), rightChild(right), bf(0)
	{}
	~AVLNode()
	{}
private:
	Type data;
	AVLNode* leftChild;
	AVLNode* rightChild;
	int      bf;  //平衡因子
};

2.AVL树的插入

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

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

整体插入流程思路:

bool AVLTree<Type>::Insert(AVLNode<Type>*& t, const Type& x)
{
	//先按Bst树插入节点 	
	AVLNode<Type>* p = t;
	stack< AVLNode<Type>*> stk;  //用一个栈记住插入时经过的路线,方便取出父节点
	AVLNode<Type>* pr = nullptr;  //父节点

	while (p != nullptr) {   //寻找插入位置
		if (x == p->data)
			return false;
		pr = p;
		stk.push(pr);
		if (x > p->data) {
			p = p->rightChild;
			continue;
		}	
		if (x < p->data)
			p = p->leftChild;
	}
					
	/////已经找到位置,开始插入
	p =new AVLNode<Type>(x);
	if (pr == nullptr) {   //空树则直接创建根节点返回
		t = p;
		return true;
	}
	if (x > pr->data)
		pr->rightChild = p;
	else
		pr->leftChild = p;

	//////插入完,调整平衡因子///  若在pr左数插入  pr->bf  -1  ; 在pr右树插入 bf->bf  +1
	while (!stk.empty()) {
		pr = stk.top();  
		stk.pop();
		if (pr->leftChild==p)  // 左数插入 pr->bf  -1
			pr->bf --;
		else
			pr->bf ++;
		
		if (pr->bf == 0) //树是平衡的  返回
			break;
		else if (pr->bf == 1 || pr->bf == -1)  // 向上回溯  pr的父节点处可能不平衡
		{
			p = pr;	    //开始下一轮while()循环,判断pr的父节点是否平衡,下一轮的pr为这一轮pr的父节点
		}
		else {  //树不平衡  需要调整  针对不平衡的节点调整   		
			if (pr->bf < 0) {
				if (p->bf < 0) {    //  /  右单旋
					RotateR(pr);
				}
				else {   //    <    先左旋后右旋
					RotateLR(pr);
				}
			}
			else {
				if (p->bf > 0) { //    \  左单旋
					RotateL(pr);
				}
				else {//   >    先右旋后左旋
					RotateRL(pr);
				}
			}
			break;
		}
	}

其中对于调整平衡因子时 满足(pr->bf == 1 || pr->bf == -1)条件要向上回溯的问题,这里做了简单图解:
在这里插入图片描述

其中调整平衡因子时,是根据平衡因子的正负变化来决定采用那种方法调整,如图:
在这里插入图片描述

具体调整平衡因子的四种旋转方法如下;

1.右单旋

在这里插入图片描述
实现:

void RotateR(AVLNode<Type>*& ptr)
	{
		AVLNode<Type>* subR = ptr;
		ptr = subR->leftChild;
		subR->leftChild = ptr->rightChild;
		ptr->rightChild = subR;
		ptr->bf = subR->bf = 0;	
	}

图解:
向上回溯到第一个不平衡的节点ptr,然后调整这个不平衡的子树即可,其它平衡的部分不用管。
然后如图设置好ptr subR指针的位置,然后转就完事了。
这里说明一下符号:

ptr 最终要指向需要调整的这个子树的根节点,最后将ptr跟其父节点连接起来,这样是一颗完整的树了。
subL是最终要连到ptr的左树的节点。
sunR是最终要连到ptr右树的节点.

在这里插入图片描述

2.左单旋

在这里插入图片描述
实现:

	void RotateL(AVLNode<Type>*& ptr)
	{
		AVLNode<Type>* subL = ptr;
		ptr = subL->rightChild;
		subL->rightChild = ptr->leftChild;
		ptr->leftChild = subL;
		ptr->bf = subL->bf = 0;
	}

3.先右单旋再左单旋

分为两个步骤,先右旋后左旋,比较难的部分是旋转完之后的平衡因子该怎么调整,这里画了图能比较直观点看到。
实现:

void RotateRL(AVLNode<Type>*& ptr){
		AVLNode<Type>* subL = ptr;
		AVLNode<Type>* subR = ptr->rightChild;
		ptr = subR->leftChild;

		subR->leftChild =ptr->rightChild;
		ptr->rightChild = subR;
		//调整subL->bf
		if (ptr->bf <= 0)
			subL->bf = 0;
		else
			subL->bf = -1;

		subL->rightChild = ptr->leftChild;
		ptr->leftChild = subL;
		
		//调整subR->bf
		if (ptr->bf >= 0)
			subR->bf = 0;
		else
			subR->bf = 1;

		ptr->bf = 0;
	}

对于旋转之后如可调整subL->bf和调整subR->bf这里可以通过画图就能找到规律。
共有以下三种情况:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

4.先左单旋再右单旋

方法类似先右单旋再左单旋(镜像)。不想画了…
实现:

	void RotateLR(AVLNode<Type>*& ptr)
	{
		AVLNode<Type>* subR = ptr;
		AVLNode<Type>* subL = ptr->leftChild;
		ptr = subL->rightChild;

		subL->rightChild = ptr->leftChild;
		ptr->leftChild = subL;
		//调整subL->bf
		if (ptr->bf <= 0)
			subL->bf = 0;
		else
			subL->bf = -1;

		subR->leftChild = ptr->rightChild;
		ptr->rightChild = subR;

		//调整subR->bf
		if (ptr->bf >= 0)
			subR->bf = 0;
		else
			subR->bf = 1;

		ptr->bf = 0;
	}
完整代码:
template<class Type>
class AVLTree;

template<class Type>
class AVLNode
{
	friend class AVLTree<Type>;
public:
	AVLNode() :data(Type()), leftChild(nullptr), rightChild(nullptr), bf(0)
	{}
	AVLNode(Type d, AVLNode* left = nullptr, AVLNode* right = nullptr)
		:data(d), leftChild(left), rightChild(right), bf(0)
	{}
	~AVLNode()
	{}
private:
	Type data;
	AVLNode* leftChild;
	AVLNode* rightChild;
	int      bf;
};

template<class Type>
class AVLTree
{
public:
	AVLTree() : root(nullptr)
	{}
public:
	bool Insert(const Type& x)
	{
		return Insert(root, x);
	}

protected:
	bool Insert(AVLNode<Type>*& t, const Type& x);
protected:

	void RotateL(AVLNode<Type>*& ptr)
	{
		AVLNode<Type>* subL = ptr;
		ptr = subL->rightChild;
		subL->rightChild = ptr->leftChild;
		ptr->leftChild = subL;
		ptr->bf = subL->bf = 0;
	}


	void RotateR(AVLNode<Type>*& ptr)
	{
		AVLNode<Type>* subR = ptr;
		ptr = subR->leftChild;
		subR->leftChild = ptr->rightChild;
		ptr->rightChild = subR;
		ptr->bf = subR->bf = 0;
	
	}


	void RotateLR(AVLNode<Type>*& ptr)
	{
		AVLNode<Type>* subR = ptr;
		AVLNode<Type>* subL = ptr->leftChild;
		ptr = subL->rightChild;

		subL->rightChild = ptr->leftChild;
		ptr->leftChild = subL;
		//调整subL bf
		if (ptr->bf <= 0)
			subL->bf = 0;
		else
			subL->bf = -1;

		subR->leftChild = ptr->rightChild;
		ptr->rightChild = subR;

		//调整subR bf
		if (ptr->bf >= 0)
			subR->bf = 0;
		else
			subR->bf = 1;

		ptr->bf = 0;
	}

	void RotateRL(AVLNode<Type>*& ptr){
		AVLNode<Type>* subL = ptr;
		AVLNode<Type>* subR = ptr->rightChild;
		ptr = subR->leftChild;

		subR->leftChild =ptr->rightChild;
		ptr->rightChild = subR;
		//调整subL bf
		if (ptr->bf <= 0)
			subL->bf = 0;
		else
			subL->bf = -1;

		subL->rightChild = ptr->leftChild;
		ptr->leftChild = subL;
		
		//调整subR bf
		if (ptr->bf >= 0)
			subR->bf = 0;
		else
			subR->bf = 1;

		ptr->bf = 0;
	}

private:
	AVLNode<Type>* root;
};

template<class Type>
bool AVLTree<Type>::Insert(AVLNode<Type>*& t, const Type& x)
{
	//先按Bst树插入节点 

	
	AVLNode<Type>* p = t;
	stack< AVLNode<Type>*> stk;  //用一个栈记住插入时经过的路线
	AVLNode<Type>* pr = nullptr;

	while (p != nullptr) {   //寻找插入位置
		if (x == p->data)
			return false;
		pr = p;
		stk.push(pr);
		if (x > p->data) {
			p = p->rightChild;
			continue;
		}	
		if (x < p->data)
			p = p->leftChild;
	}
				
	/////插入
	p =new AVLNode<Type>(x);
	if (pr == nullptr) {   //空树则直接创建根节点返回
		t = p;
		return true;
	}

	if (x > pr->data)
		pr->rightChild = p;
	else
		pr->leftChild = p;

	//////////调整平衡因子///  左数插入  bf -1  ; 右树插入 bf +1
	while (!stk.empty()) {
		pr = stk.top();
		stk.pop();

		if (pr->leftChild==p)  // 左数插入  bf -1 
			pr->bf --;
		else
			pr->bf ++;
		
		if (pr->bf == 0) //树是平衡的  返回
			break;
		else if (pr->bf == 1 || pr->bf == -1)  // 向上回溯  上面可能会不平衡
		{
			p = pr;	
		}
		else {  //树不平衡  需要调整  针对不平衡的节点调整

			if (pr->bf < 0) {
				if (p->bf < 0) {    //  /  右单旋
					RotateR(pr);
				}
				else {   //    <    先左旋后右旋
					RotateLR(pr);
				}
			}
			else {
				if (p->bf > 0) { //    \  左单旋
					RotateL(pr);
				}
				else {//   >    先右旋后左旋
					RotateRL(pr);
				}
			}
			break;
		}
	}

	//连接
	if (stk.empty())
		t = pr;
	else
	{
		AVLNode<Type>* q = stk.top();
		if (pr->data < q->data)
			q->leftChild = pr;
		else
			q->rightChild = pr;
	}

	return true;
}


int main()
{
	vector<int> iv{ 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	AVLTree<int> avl;

	for (const auto& e : iv)
		avl.Insert(e);
	return 0;
}
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值