AVL树(三叉链和二叉链)


一、AVL树的基本概念

二叉搜索树能够实现高效查找O(logN)。但是遇到特殊情况,比如升序或者降序就会退化为单支树,此时查找的效率就退化到了O(N)。

AVL树又称平衡搜索树,它保证了当向搜索树中插入新的节点后,如果能使每个节点的左右子树高度只差绝对值不超过1,即保证了这棵树是平衡的,即有如下特点:

  • 左右子树都是平衡的(高度差的绝对值不超过1)
  • 如果一棵二叉搜索树的高度是平衡的,它就是AVL树。它的查找效率为logn

1.1. 平衡因子

为了实现搜索树的平衡,引入了平衡因子的概念(当然也有别的方法实现平衡,比如树的高度,使用平衡因子会更方便一些)。每个节点都有一个平衡因子它是该节点右子树高度与左子树高度的差(也可以是左子树与右子树的高度差,区别不大),如果平衡因子为0,-1或1,则说明该节点的左右子树平衡,如果为2,-2则说明不平衡,此时我们需要对该节点的左右子树进行旋转操作。
在这里插入图片描述

1.2. 平衡因子的调节

以右子树高度减左子树高度的平衡因子为例:

  1. 新增的节点在父节点的右侧时parent->bf++;

  2. 新增的节点再父节点的左侧时 parent->bf--;

  3. 更新平衡因子之后、如果parent->bf==0说明以当前节点为根的子树是平衡的,即不会再对上一层的父节点的平衡产生影响.
    在这里插入图片描述

  4. 当节点的平衡因子为1或者-1的时候,说明新增的节点对父节点的平衡产生了影响(因为原来该节点的平衡因子为0,左右子树高度相等,变为1或-1说明该节点的左子树或右子树的高度变了,以该节点为根的树的高度变了),此时需要向上继续调节平衡因子,直到平衡因子为0或到根节点为止。

  5. 当节点的平衡因子为2或者-2的时候,说明此时以该节点为根的树,已经失去了平衡,此时需要进行旋转处理


二、三叉链的AVL树

2.1. 旋转的四种情况

右单旋

=

右单旋的判断条件

  1. parent->bf=-2表示左子树高
  2. cur->bf=-1表示新插入的节点在当前节点的左侧

处理方式

  1. 此时左边高、右边低,因此需要使右边的高度增加,左边的高度降低
  2. b树位于30的右侧,因此按照搜索树树的性质,是可以转移到60的左侧的
  3. 将b树转移之后,再将60链接在30的右侧,此时30左右两侧的高度都是h+1,即平衡了

注意
在旋转时,parent的父节点pparent也需要进行修改,让其指向cur,当根节点就是parent时,此时让cur作根节点。
在这里插入图片描述

void RotateR(Node* parent)
{
	Node* cur = parent->_left;
	Node* curR = cur->_right;//cur的右子树
	Node* pparent = parent->_parent;//保存parent的父亲节点

	//将cur右子树链接到parent的左侧
	parent->_left = curR;
	if (curR)
		curR->_parent = parent;

	//将parent连接到cur的右侧
	cur->_right = parent;
	parent->_parent = cur;

	//将cur与pparent链接起来
	if (pparent == nullptr)//cur变成新的根
	{
		_root = cur;
		cur->_parent = nullptr;
	}
	else//pparent不为根
	{
		cur->_parent = pparent;
		if (parent == pparent->_left)//在上一级节点的左侧
		{
			pparent->_left = cur;
		}
		else
		{
			pparent->_right = cur;
		}
	}

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

}

左单旋

左单旋和右单旋差不多:
在这里插入图片描述

左单旋的判断条件:

  1. parent->bf==2说明右子树高了
  2. cur->vf==1说明在当前节点的右侧增加了一个新的节点(1只能是由0变来的)

处理方式

  1. 此时左低右高,因此需要将左边提高,右边降低
  2. b位于30的右侧,60的左侧,挪动之后不会影响BS的性质
  3. 将30往下压,60往上提升之后得到以60位根的树的左右两侧的高度都是h+1
void RotateL(Node* parent)//左旋
{
	Node* cur = parent->_right;//右变高,不可能为空
	Node* curL = cur->_left;
	Node* pparent = parent->_parent;

	//cur左子树作为parent右子树
	parent->_right = curL;
	if (curL)
		curL->_parent = parent;

	//parent作为cur左子树
	cur->_left = parent;
	parent->_parent = cur;

	//cur链接到pprent上
	if (pparent == nullptr)//根
	{
		_root = cur;
		cur->_parent = nullptr;
	}
	else//不为根
	{
		cur->_parent = pparent;
		//判断链接在哪一侧
		if (pparent->_left == parent)
		{
			pparent->_left = cur;
		}
		else
		{
			pparent->_right = cur;
		}
	}

	//平衡因子的更新
	parent->_bf = 0;
	cur->_bf = 0;
}

左右双旋

在这里插入图片描述

此时无论是左旋、或者右旋都无法使树平衡,因为左旋和右旋只适用于平衡因子变化是直线的情况,这里平衡因子是折线,此时可以先对30进行左旋,然后对90进行右旋:

在这里插入图片描述

对于在c插入的情况也是一样,只不过最终的平衡因子不一样:

在这里插入图片描述

判断条件:

  1. parent -> bf=-2时,此时表示左高右低
  2. cur-> bf =1,表示当前cur节点的右高左低,此时新插入的节点对树的平衡因子变化为“折线”。
void RotateLR(Node* parent)
{
	Node* cur = parent->_left;
	Node* curR = cur->_right;//此时不可能为空,因为右子树高

	int bf = curR->_bf;//保存一份平衡因子

	RotateL(cur);//先左旋
	RotateR(parent);//再右旋

	//左旋、右旋会将平衡因子全部处理成0,因此要对平衡因子进行更改

	if (bf == 1)//在curR的右侧插入
	{
		curR->_bf = 0;
		cur->_bf = -1;
		parent->_bf = 0;
	}
	else if (bf == -1)//在curR左侧插入
	{
		curR->_bf = 0;
		cur->_bf = 0;
		parent->_bf = 1;
	}
}

右左双旋

在这里插入图片描述

右左双旋的情况和左右双旋类似:
在这里插入图片描述

在这里插入图片描述

判断条件:

  1. parent -> bf=2时,此时表示右高左低
  2. cur-> bf =-1,表示当前cur节点的左高右低,此时新插入的节点对树的平衡因子变化为“折线”。
//右左双旋
void RotateRL(Node* parent)
{
	Node* cur = parent->_right;
	Node* curL = cur->_left;

	int bf = curL->_bf;

	RotateR(cur);//先右旋
	RotateL(parent);//再左旋

	//平衡因子出来

	if (bf == 1)//在subRL右侧插入时
	{
		curL->_bf = 0;
		parent->_bf = -1;
		cur->_bf = 0;
	}
	else if (bf == -1)//在左侧插入时
	{
		curL->_bf = 0;
		parent->_bf = 0;
		cur->_bf = 1;
	}
}

2.2. 节点类的创建以及构造函数(KV模型)

//单个节点
template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode(const pair<K, V>& kv = pair<K, V>())
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
		, _kv(kv)
	{}
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	int _bf;//balance factor 平衡因子

	pair<K, V> _kv;
};
//AVL树
template<class K, class V>
class AVLTree
{
public:
	typedef struct AVLTreeNode<K, V>  Node;

private:
	Node* _root = nullptr;
};

pair将2个数据组合成一组数据, pair的实现是一个结构体,主要的两个成员变量是first和second,因为是使用struct不是class,所以可以直接使用pair的成员变量。

2.3. 插入

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 (kv.first < cur->_kv.first)//K值比较,小于往左边走
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (kv.first > cur->_kv.first)//往右走
		{
			parent = cur;
			cur = cur->_right;
		}
		else//相等,不进行插入
		{
			return false;
		}
	}
	//此时已经找到插入的位置了,判断插入在parent的左边还是右边
	cur = new Node(kv);
	if (parent->_kv.first > kv.first)//插在左边
	{
		parent->_left = cur;
		cur->_parent = parent;//三叉链,cur父指针回指
	}
	else//插在右边
	{
		parent->_right = cur;
		cur->_parent = parent;//三叉链,cur父指针回指
	}
	//更新平衡因子
	while (parent)//不为空
	{
		if (parent->_left == cur)//cur在parent左侧
		{
			parent->_bf--;
		}
		else//cur在parent右侧
		{
			parent->_bf++;
		}

		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)//左边高
			{
				if (cur->_bf == -1)//在当前节点的左侧插入了节点 ->右单旋
				{
					RotateR(parent);
				}
				else//cur->_bf=1 ->平衡因子为折线,需要进行左右双旋
				{
					RotateLR(parent);
				}
			}
			else//右边高
			{
				if (cur->_bf == 1)//在当前节点的右侧插入了节点 ->  左单旋
				{
					RotateL(parent);
				}
				else//cur->_bf=-1 平衡因子为折线,需要进行右左双旋
				{
					RotateRL(parent);
				}
			}
			break;//旋转过后当前的树就是平衡的了,退出
		}
		else//不可能走到这一步,走到这里说明程序发生了逻辑错误
		{
			exit(-1);
		}
	}
	return true;
}

2.4. 删除

删除操作和插入操作有些类似:

  1. 右边插入,父亲平衡因子++,左边插入,父亲平衡因子–
    右边删除,父亲平衡因子–,左边删除,父亲平衡因子++

  2. 插入后,父亲的平衡因子变为0,说明父亲所在的树高度不变,更新结束
    删除后,父亲的平衡因子变为0,说明父亲所在的树高度变了(因为删除前是1或-1),继续往上更新

  3. 插入后,父亲的平衡因子变为1或-1,说明父亲所在的树的高度变了,继续往上更新
    删除后,父亲的平衡因子变为1或-1,说明父亲所在的树的高度不变,更新结束

  4. 插入/删除后,父亲的平衡因子变为2或-2,说明不平衡,需要进行旋转

不过旋转的时候,cur的平衡因子有三种情况,分别为1,0,-1,当cur的平衡因子为1或-1时,可以调用上面的四种旋转情况,但是当cur为0时,上面的四种情况就行不通了,原因是旋转后的平衡因子不匹配,此时左旋或右旋以后需要手动更新其平衡因子:

在这里插入图片描述

cur为0时旋转完并手动更新完平衡因子后,此时整棵树的高度并没有变化,因此不需要向上更新平衡因此。
其他情况由于旋转操作使树的高度发生了改变,因此旋转会影响其父结点的平衡因子,因此我们还需要继续往上更新平衡因子。

下面是删除的函数:

//删除函数
bool Erase(const K& key)
{
	//用于遍历二叉树
	Node* parent = nullptr;
	Node* cur = _root;
	//用于标记实际的删除结点及其父结点
	Node* delParentPos = nullptr;
	Node* delPos = nullptr;
	while (cur)
	{
		if (key < cur->_kv.first) //所给key值小于当前结点的key值
		{
			//往该结点的左子树走
			parent = cur;
			cur = cur->_left;
		}
		else if (key > cur->_kv.first) //所给key值小于当前结点的key值
		{
			//往该结点的右子树走
			parent = cur;
			cur = cur->_right;
		}
		else //找到了待删除结点
		{
			if (cur->_left == nullptr) //待删除结点的左子树为空
			{
				if (cur == _root) //待删除结点是根结点
				{
					_root = _root->_right; //让根结点的右子树作为新的根结点
					//如果此时_root为空,此时寻找去父亲指针会对空指针解引用而报错,因此需要判断
					if (_root)
						_root->_parent = nullptr;
					delete cur; //删除原根结点
					return true; //根结点无祖先结点,无需进行平衡因子的更新操作
				}
				else
				{
					delParentPos = parent; //标记实际删除结点的父结点
					delPos = cur; //标记实际删除的结点
				}
				break; //删除结点有祖先结点,需更新平衡因子
			}
			else if (cur->_right == nullptr) //待删除结点的右子树为空
			{
				if (cur == _root) //待删除结点是根结点
				{
					_root = _root->_left; //让根结点的左子树作为新的根结点
					//如果此时_root为空,此时寻找去父亲指针会对空指针解引用而报错,因此需要判断
					if (_root)
						_root->_parent = nullptr;
					delete cur; //删除原根结点
					return true; //根结点无祖先结点,无需进行平衡因子的更新操作
				}
				else
				{
					delParentPos = parent; //标记实际删除结点的父结点
					delPos = cur; //标记实际删除的结点
				}
				break; //删除结点有祖先结点,需更新平衡因子
			}
			else //待删除结点的左右子树均不为空
			{
				//替换法删除
				//寻找待删除结点右子树当中key值最小的结点作为实际删除结点
				Node* minParent = cur;
				Node* minRight = cur->_right;
				while (minRight->_left)
				{
					minParent = minRight;
					minRight = minRight->_left;
				}
				cur->_kv.first = minRight->_kv.first; //将待删除结点的key改为minRight的key
				cur->_kv.second = minRight->_kv.second; //将待删除结点的value改为minRight的value
				delParentPos = minParent; //标记实际删除结点的父结点
				delPos = minRight; //标记实际删除的结点
				break; //删除结点有祖先结点,需更新平衡因子
			}
		}
	}
	if (delParentPos == nullptr) //delParentPos没有被修改过,说明没有找到待删除结点
	{
		return false;
	}

	//记录待删除结点及其父结点
	Node* del = delPos;
	Node* delP = delParentPos;

	//更新平衡因子
	while (delPos != _root) 
	{
		//判断删除的是在父亲的哪一边,然后更新平衡因子
		if (delPos == delParentPos->_left) 
		{
			delParentPos->_bf++; 
		}
		else if (delPos == delParentPos->_right) 
		{
			delParentPos->_bf--; 
		}
		//当前树是平衡的,停止更新
		if (delParentPos->_bf == -1 || delParentPos->_bf == 1)
		{
			break; //delParent树的高度没有发生变化,不会影响其父结点及以上结点的平衡因子
		}
		else if (delParentPos->_bf == 0)//需要继续往上更新平衡因子
		{
			//delParentPos树的高度变化,会影响其父结点的平衡因子,需要继续往上更新平衡因子
			delPos = delParentPos;
			delParentPos = delParentPos->_parent;
		}
	
		else if (delParentPos->_bf == -2 || delParentPos->_bf == 2) //需要进行旋转处理
		{
			if (delParentPos->_bf == -2)//左边高
			{
				if (delParentPos->_left->_bf == -1)//左边高,进行右单旋
				{
					Node* tmp = delParentPos->_left; //记录delParentPos右旋转后新的根结点
					RotateR(delParentPos); //右单旋
					delParentPos = tmp; //更新根结点
				}
				else if (delParentPos->_left->_bf == 1)//曲线影响,需要进行左右双旋
				{
					Node* tmp = delParentPos->_left->_right; //记录delParentPos左右旋转后新的根结点
					RotateLR(delParentPos); //左右双旋
					delParentPos = tmp; //更新根结点
				}
				else //delParentPos->_left->_bf == 0
				{
					Node* tmp = delParentPos->_left; //记录delParentPos右旋转后新的根结点
					RotateR(delParentPos); //右单旋
					delParentPos = tmp; //更新根结点
					//平衡因子调整
					delParentPos->_bf = 1;
					delParentPos->_right->_bf = -1;
					break; 
				}
			}
			else //delParentPos->_bf == 2
			{
				if (delParentPos->_right->_bf == -1)
				{
					Node* tmp = delParentPos->_right->_left; //记录delParentPos右左旋转后新的根结点
					RotateRL(delParentPos); //右左双旋
					delParentPos = tmp; //更新根结点
				}
				else if (delParentPos->_right->_bf == 1)//曲线影响,需要进行左右双旋
				{
					Node* tmp = delParentPos->_right; //记录delParentPos左旋转后新的根结点
					RotateL(delParentPos); //左单旋
					delParentPos = tmp; //更新根结点
				}
				else //delParentPos->_right->_bf == 0
				{
					Node* tmp = delParentPos->_right; //记录delParentPos左旋转后新的根结点
					RotateL(delParentPos); //左单旋
					delParentPos = tmp; //更新根结点
					//平衡因子调整
					delParentPos->_bf = -1;
					delParentPos->_left->_bf = 1;
					break; 
				}
			}
			//继续往上更新平衡因子
			delPos = delParentPos;
			delParentPos = delParentPos->_parent;
		}
		else
		{
			exit(-1); //不会到这里,到这里说明出现了错误
		}
	}
	//进行实际删除
	if (del->_left == nullptr) //实际删除结点的左子树为空
	{
		if (del == delP->_left) //实际删除结点是其父结点的左孩子
		{
			delP->_left = del->_right;
			if (del->_right)
				del->_right->_parent = parent;
		}
		else //实际删除结点是其父结点的右孩子
		{
			delP->_right = del->_right;
			if (del->_right)
				del->_right->_parent = parent;
		}
	}
	else //实际删除结点的右子树为空
	{
		if (del == delP->_left) //实际删除结点是其父结点的左孩子
		{
			delP->_left = del->_left;
			if (del->_left)
				del->_left->_parent = parent;
		}
		else //实际删除结点是其父结点的右孩子
		{
			delP->_right = del->_left;
			if (del->_left)
				del->_left->_parent = parent;
		}
	}
	delete del; //实际删除结点
	return true;
}

三、三叉链AVL树的代码以及测试

#pragma once
using namespace std;
#include <iostream>
#include<queue>
template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode(const pair<K, V>& kv = pair<K, V>())
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
		, _kv(kv)
	{}
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	int _bf;//balance factor 平衡因子

	pair<K, V> _kv;
};

template<class K, class V>
class AVLTree
{
public:
	typedef struct AVLTreeNode<K, V>  Node;
	//右单旋
	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curR = cur->_right;//cur的右子树
		Node* pparent = parent->_parent;//保存parent的父亲节点

		//将cur右子树链接到parent的左侧
		parent->_left = curR;
		if (curR)
			curR->_parent = parent;

		//将parent连接到cur的右侧
		cur->_right = parent;
		parent->_parent = cur;

		//将cur与pparent链接起来
		if (pparent == nullptr)//cur变成新的根
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else//pparent不为根
		{
			cur->_parent = pparent;
			if (parent == pparent->_left)//parent在父亲节点的左侧
			{
				pparent->_left = cur;
			}
			else
			{
				pparent->_right = cur;
			}
		}

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

	}
	//左单旋
	void RotateL(Node* parent)//左旋
	{
		Node* cur = parent->_right;//右变高,不可能为空
		Node* curL = cur->_left;
		Node* pprent = parent->_parent;

		//curL连接到parent上
		parent->_right = curL;
		if (curL)
			curL->_parent = parent;

		//parent连接到cur上
		cur->_left = parent;
		parent->_parent = cur;

		//cur链接到pprent上
		if (pprent == nullptr)//根
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else//不为根
		{
			cur->_parent = pprent;
			//判断链接在哪一侧
			if (pprent->_left == parent)
			{
				pprent->_left = cur;
			}
			else
			{
				pprent->_right = cur;
			}
		}

		//平衡因子的更新
		parent->_bf = 0;
		cur->_bf = 0;
	}

	//左右双旋
	void RotateLR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curR = cur->_right;//此时不可能为空,因为右子树高

		int bf = curR->_bf;//保存一份平衡因子

		RotateL(cur);//先左旋
		RotateR(parent);//再右旋

		//左旋、右旋会将平衡因子全部处理成0,因此要对平衡因子进行更改

		if (bf == 1)//在curR的右侧插入
		{
			curR->_bf = 0;
			cur->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == -1)//在curR左侧插入
		{
			curR->_bf = 0;
			cur->_bf = 0;
			parent->_bf = 1;
		}
	}
	//右左双旋
	void RotateRL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curL = cur->_left;

		int bf = curL->_bf;

		RotateR(cur);//先右旋
		RotateL(parent);//再左旋

		//平衡因子出来

		if (bf == 1)//在subRL右侧插入时
		{
			curL->_bf = 0;
			parent->_bf = -1;
			cur->_bf = 0;
		}
		else if (bf == -1)//在左侧插入时
		{
			curL->_bf = 0;
			parent->_bf = 0;
			cur->_bf = 1;
		}
	}
	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 (kv.first < cur->_kv.first)//K值比较,小于往左边走
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kv.first > cur->_kv.first)//往右走
			{
				parent = cur;
				cur = cur->_right;
			}
			else//相等,不进行插入
			{
				return false;
			}
		}

		//此时已经找到插入的位置了,判断插入在parent的左边还是右边
		cur = new Node(kv);
		if (parent->_kv.first > kv.first)//插在左边
		{
			parent->_left = cur;
			cur->_parent = parent;//三叉链,cur父指针回指
		}
		else//插在右边
		{
			parent->_right = cur;
			cur->_parent = parent;//三叉链,cur父指针回指
		}


		//更新平衡因子
		while (parent)//不为空
		{
			if (parent->_left == cur)//cur在parent左侧
			{
				parent->_bf--;
			}
			else//cur在parent右侧
			{
				parent->_bf++;
			}

			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)//左边高
				{
					if (cur->_bf == -1)//是在当前节点的左侧插入了节点 ->右单旋
					{
						RotateR(parent);
					}
					else//cur->_bf=1 ->曲线影响,需要进行左右双旋
					{
						RotateLR(parent);
					}
				}
				else//右边高
				{
					if (cur->_bf == 1)//在当前节点的右侧插入了节点 ->  左单旋
					{
						RotateL(parent);
					}
					else//cur->_bf=-1 曲线影响
					{
						RotateRL(parent);
					}
				}
				break;//旋转过后当前的树就是平衡的了,退出
			}
			else//0 1 2 -> 不可能走到这一步,走到这里说明发生了逻辑错误
			{
				exit(-1);
			}
		}
		return true;
	}

	//删除函数
	bool Erase(const K& key)
	{
		//用于遍历二叉树
		Node* parent = nullptr;
		Node* cur = _root;
		//用于标记实际的删除结点及其父结点
		Node* delParentPos = nullptr;
		Node* delPos = nullptr;
		while (cur)
		{
			if (key < cur->_kv.first) //所给key值小于当前结点的key值
			{
				//往该结点的左子树走
				parent = cur;
				cur = cur->_left;
			}
			else if (key > cur->_kv.first) //所给key值小于当前结点的key值
			{
				//往该结点的右子树走
				parent = cur;
				cur = cur->_right;
			}
			else //找到了待删除结点
			{
				if (cur->_left == nullptr) //待删除结点的左子树为空
				{
					if (cur == _root) //待删除结点是根结点
					{
						_root = _root->_right; //让根结点的右子树作为新的根结点
						//如果此时_root为空,此时寻找去父亲指针会对空指针解引用而报错,因此需要判断
						if (_root)
							_root->_parent = nullptr;
						delete cur; //删除原根结点
						return true; //根结点无祖先结点,无需进行平衡因子的更新操作
					}
					else
					{
						delParentPos = parent; //标记实际删除结点的父结点
						delPos = cur; //标记实际删除的结点
					}
					break; //删除结点有祖先结点,需更新平衡因子
				}
				else if (cur->_right == nullptr) //待删除结点的右子树为空
				{
					if (cur == _root) //待删除结点是根结点
					{
						_root = _root->_left; //让根结点的左子树作为新的根结点
						//如果此时_root为空,此时寻找去父亲指针会对空指针解引用而报错,因此需要判断
						if (_root)
							_root->_parent = nullptr;
						delete cur; //删除原根结点
						return true; //根结点无祖先结点,无需进行平衡因子的更新操作
					}
					else
					{
						delParentPos = parent; //标记实际删除结点的父结点
						delPos = cur; //标记实际删除的结点
					}
					break; //删除结点有祖先结点,需更新平衡因子
				}
				else //待删除结点的左右子树均不为空
				{
					//替换法删除
					//寻找待删除结点右子树当中key值最小的结点作为实际删除结点
					Node* minParent = cur;
					Node* minRight = cur->_right;
					while (minRight->_left)
					{
						minParent = minRight;
						minRight = minRight->_left;
					}
					cur->_kv.first = minRight->_kv.first; //将待删除结点的key改为minRight的key
					cur->_kv.second = minRight->_kv.second; //将待删除结点的value改为minRight的value
					delParentPos = minParent; //标记实际删除结点的父结点
					delPos = minRight; //标记实际删除的结点
					break; //删除结点有祖先结点,需更新平衡因子
				}
			}
		}
		if (delParentPos == nullptr) //delParentPos没有被修改过,说明没有找到待删除结点
		{
			return false;
		}

		//记录待删除结点及其父结点
		Node* del = delPos;
		Node* delP = delParentPos;

		//更新平衡因子
		while (delPos != _root) 
		{
			//判断删除的是在父亲的哪一边,然后更新平衡因子
			if (delPos == delParentPos->_left) 
			{
				delParentPos->_bf++; 
			}
			else if (delPos == delParentPos->_right) 
			{
				delParentPos->_bf--; 
			}
			//当前树是平衡的,停止更新
			if (delParentPos->_bf == -1 || delParentPos->_bf == 1)
			{
				break; //delParent树的高度没有发生变化,不会影响其父结点及以上结点的平衡因子
			}
			else if (delParentPos->_bf == 0)//需要继续往上更新平衡因子
			{
				//delParentPos树的高度变化,会影响其父结点的平衡因子,需要继续往上更新平衡因子
				delPos = delParentPos;
				delParentPos = delParentPos->_parent;
			}
		
			else if (delParentPos->_bf == -2 || delParentPos->_bf == 2) //需要进行旋转处理
			{
				if (delParentPos->_bf == -2)//左边高
				{
					if (delParentPos->_left->_bf == -1)//左边高,进行右单旋
					{
						Node* tmp = delParentPos->_left; //记录delParentPos右旋转后新的根结点
						RotateR(delParentPos); //右单旋
						delParentPos = tmp; //更新根结点
					}
					else if (delParentPos->_left->_bf == 1)//曲线影响,需要进行左右双旋
					{
						Node* tmp = delParentPos->_left->_right; //记录delParentPos左右旋转后新的根结点
						RotateLR(delParentPos); //左右双旋
						delParentPos = tmp; //更新根结点
					}
					else //delParentPos->_left->_bf == 0
					{
						Node* tmp = delParentPos->_left; //记录delParentPos右旋转后新的根结点
						RotateR(delParentPos); //右单旋
						delParentPos = tmp; //更新根结点
						//平衡因子调整
						delParentPos->_bf = 1;
						delParentPos->_right->_bf = -1;
						break; 
					}
				}
				else //delParentPos->_bf == 2
				{
					if (delParentPos->_right->_bf == -1)
					{
						Node* tmp = delParentPos->_right->_left; //记录delParentPos右左旋转后新的根结点
						RotateRL(delParentPos); //右左双旋
						delParentPos = tmp; //更新根结点
					}
					else if (delParentPos->_right->_bf == 1)//曲线影响,需要进行左右双旋
					{
						Node* tmp = delParentPos->_right; //记录delParentPos左旋转后新的根结点
						RotateL(delParentPos); //左单旋
						delParentPos = tmp; //更新根结点
					}
					else //delParentPos->_right->_bf == 0
					{
						Node* tmp = delParentPos->_right; //记录delParentPos左旋转后新的根结点
						RotateL(delParentPos); //左单旋
						delParentPos = tmp; //更新根结点
						//平衡因子调整
						delParentPos->_bf = -1;
						delParentPos->_left->_bf = 1;
						break; 
					}
				}
				//继续往上更新平衡因子
				delPos = delParentPos;
				delParentPos = delParentPos->_parent;
			}
			else
			{
				exit(-1); //不会到这里,到这里说明出现了错误
			}
		}
		//进行实际删除
		if (del->_left == nullptr) //实际删除结点的左子树为空
		{
			if (del == delP->_left) //实际删除结点是其父结点的左孩子
			{
				delP->_left = del->_right;
				if (del->_right)
					del->_right->_parent = parent;
			}
			else //实际删除结点是其父结点的右孩子
			{
				delP->_right = del->_right;
				if (del->_right)
					del->_right->_parent = parent;
			}
		}
		else //实际删除结点的右子树为空
		{
			if (del == delP->_left) //实际删除结点是其父结点的左孩子
			{
				delP->_left = del->_left;
				if (del->_left)
					del->_left->_parent = parent;
			}
			else //实际删除结点是其父结点的右孩子
			{
				delP->_right = del->_left;
				if (del->_left)
					del->_left->_parent = parent;
			}
		}
		delete del; //实际删除结点
		return true;
	}
	
	//遍历的时候 root为private外面无法拿到
	//因此需要封装一层
	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		cout << root->_kv.first << " " << root->_kv.second << endl;
		_Inorder(root->_right);
	}
	//中序遍历
	void Inorder()
	{
		_Inorder(_root);
	}
	
	Node* Find(const K& k)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first > k)
				cur = cur->_left;
			else if (cur->_kv.first < k)
				cur = cur->_right;
			else
				return cur;
		}
		return false;
	}


	//求树的深度
	int maxDepth(Node* root)
	{
		if (root == NULL)
		{
			return 0;
		}
		int leftDepth = maxDepth(root->_left);
		int rightDepth = maxDepth(root->_right);
		return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
	}
	bool _isBalanced(Node* root) 
	{
		if (root == nullptr)
		{
			return true;
		}
		int leftHight = maxDepth(root->_left);
		int rightHight = maxDepth(root->_right);
		return abs(leftHight - rightHight) < 2
			&&_isBalanced(root->_left)
			&&_isBalanced(root->_right);

	}
	bool isBalanced() 
	{
		return _isBalanced(_root);

	}
private:
	Node* _root = nullptr;
};

在这里插入图片描述


四、二叉链的AVL树(严蔚敏版)(K模型)

严蔚敏版的二叉链AVL树的平衡因子采用的是左减右的方式

由于是二叉链,没有父亲节点,这就导致没法直接向上调节平衡因子,严蔚敏给出的思路是采用递归的方式。

这里直接给出代码:

#include <iostream>
using namespace std;

#define LH +1
#define EH 0
#define RH -1

typedef struct BSTNode
{
	int data;
	int bf;
	struct BSTNode* lchild, * rchild;
}BSTNode, * BSTree;

void R_Rotate(BSTree& p)
{
	BSTree lc;

	lc = p->lchild;
	p->lchild = lc->rchild;
	lc->rchild = p;
	p = lc;

	return;
}

void L_Rotate(BSTree& p)
{
	BSTree rc;
	rc = p->rchild;
	p->rchild = rc->lchild;
	rc->lchild = p;
	p = rc;

	return;
}
void LeftBalance(BSTree& T)
{
	//对指针T所致节点为根的二叉树作平衡旋转处理
	BSTree lc;
	lc = T->lchild;
	//检查T节点的左孩子,根据其平衡因子判断是右旋还是左右双旋
	switch (lc->bf)
	{
	//左孩子的平衡因子为1,平衡因子是直线,右旋
	case LH:
		T->bf = EH;
		lc->bf = EH;
		R_Rotate(T);
		break;
	//左孩子平衡因子为-1,平衡因子为折线,左右双旋
	case RH:
		BSTree rd;
		rd = lc->rchild;
		//修改T节点和其左孩子的平衡因子
		switch (rd->bf)
		{
		case LH:
			T->bf = RH;
			lc->bf = EH;
			break;
		case EH:
			T->bf = lc->bf = EH;
			break;
		case RH:
			lc->bf = LH;
			T->bf = EH;
			break;
		}
		rd->bf = EH;
		L_Rotate(T->lchild);
		R_Rotate(T);
	}
}
void RightBalance(BSTree& T)
{
	//对指针T所致节点为根的二叉树作平衡旋转处理
	BSTree rc;

	rc = T->rchild;
	//检查T节点的右孩子,根据其平衡因子判断是左旋还是右左双旋
	switch (rc->bf)
	{
	//右孩子的平衡因子为-1,平衡因子是直线,左旋
	case RH:
		T->bf = EH;
		rc->bf = EH;
		L_Rotate(T);
		break;
	//右孩子平衡因子为-1,平衡因子为折线,右左双旋
	case LH:
		BSTree ld;
		ld = rc->lchild;
		//修改T节点和其右孩子的平衡因子
		switch (ld->bf)
		{
		case LH:
			T->bf = EH;
			rc->bf = RH;
			break;
		case EH:
			T->bf = rc->bf = EH;
			break;
		case RH:
			T->bf = LH;
			rc->bf = EH;
			break;
		}
		ld->bf = EH;
		R_Rotate(T->rchild);
		L_Rotate(T);
	}
}

int InsertAVL(BSTree& T, int e, bool& taller)
{
	if (!T)
	{
		T = (BSTree)malloc(sizeof(BSTNode));
		T->data = e;
		T->lchild = NULL;
		T->rchild = NULL;
		T->bf = EH;
		taller = true;
	}
	else
	{
		//树中已存在和e相同的节点,返回0
		if (T->data == e)
		{
			taller = false;
			return 0;
		}
		//继续在左子树中搜索
		if (T->data > e)
		{
			//说明递归插入失败了
			if (!InsertAVL(T->lchild, e, taller))
			{
				taller = false;
				return 0;
			}
			//到这里说明插入成功,判断平衡因子
			if (taller)
			{
				switch (T->bf)
				{
				//原本左子树比右子树高,再插入以后,平衡因子变为2,此时需要做左平衡处理
				case LH:
					LeftBalance(T);
					taller = false;
					break;
				//原本左右子树,等高,现因左子树增高而使树增高
				case EH:
					taller = true;
					T->bf = LH;
					break;
				//原本右子树比左子树高,现在左右子树登高
				case RH:
					taller = false;
					T->bf = EH;
					break;
				}
			}
		}
		//继续在右子树中搜索
		else
		{
			if (!InsertAVL(T->rchild, e, taller))
			{
				taller = false;
				return 0;
			}
			//到这里说明插入成功,判断平衡因子
			if (taller)
			{
				switch (T->bf)
				{
				//原本左子树比右子树高,插入以后,左右等高
				case LH:
					T->bf = EH;
					taller = false;
					break;
				//原本等高,插入以后,右子树等高
				case EH:
					T->bf = RH;
					taller = true;
					break;
				//原本右子树高,插入以后,平衡因子变为-2,需要做右平衡处理
				case RH:
					RightBalance(T);
					taller = false;
					break;
				}
			}
		}
	}

	return 1;
}

//中序遍历
void InorderTraversal(BSTree node)
{
	if (!node)
	{
		return;
	}
	else
	{
		InorderTraversal(node->lchild);
		cout << node->data << " ";
		InorderTraversal(node->rchild);
	}
	return;
}

int main(void)
{
	//flag用于判断是否插入成功,ture为成功,false为失败
	bool flag = false;
	BSTree T = NULL;


	InsertAVL(T, 7, flag);
	InsertAVL(T, 8, flag);
	InsertAVL(T, 9, flag);
	InsertAVL(T, 4, flag);
	InsertAVL(T, 5, flag);
	InsertAVL(T, 6, flag);
	InsertAVL(T, 1, flag);
	InsertAVL(T, 2, flag);
	InsertAVL(T, 3, flag);

	InorderTraversal(T);
	cout << endl;

	return 0;
}

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天也要写bug、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值