AVL树的详解与实现

之前详细讲解过了二叉搜索树(二叉搜索树详解博客链接:https://blog.csdn.net/smx_dd/article/details/86563142),但是二叉搜索树有缺陷,那就是当插入数据有序时,会退化成单支树,查找效率相当于一个链表。为了解决上述问题,有这样一种办法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
什么是AVL树?  AVL树是一颗空树,或者是具有以下性质的一棵二叉搜索树:1. 它的左右子树都是AVL树。 2. 左右子树高度之差的绝对值不超过1。

关于AVL树节点的定义,与普通二叉树不同,这里我们引入的指向其父亲结点的指针和平衡因子。
 

template<class K,class V>
struct AVLNode
{
	AVLNode(const pair<K,V>& _kv) 
	:left(nullptr)
	, right(nullptr)
	, parent(nullptr)
	, kv(_kv)
	, bf(0)
	{}
	AVLNode<K,V>* left;
	AVLNode<K, V>* right;
	AVLNode<K, V>* parent;
	pair<K,V> kv;
	int bf;
};

AVL树的插入

首先AVL是一颗二叉搜索树,所以插入规则和二叉搜索树规则一样,先是寻找合适的位置:从根结点开始,比较要插入节点的 val 和当前节点的 val 大小,如果当前节点的 val 小于要插入节点的 val ,则从当前节点的右孩子去寻找;如果当前节点的 val 大于要插入节点的 val ,则从当前节点的左孩子去寻找。
按照二叉搜索树的插入方式插入新节点之后,该AVL树的平衡性可能会被破坏,此时需要更新平衡因子,并检测是否破坏了平衡性。
在新节点插入之前,插入之后的节点的父亲节点 parent 的平衡因子一定是 -1,0,1。
1. 如果新节点插入到 parent 的左孩子,则 parent 的平衡因子减 1。
2. 如果新节点插入到 parent 的右孩子,则 parent 的平衡因子加 1。
插入新节点后,parent 的平衡因子有以下几种情况: 0,正负1,正负2。
1. 如果插入新节点后 parent 的平衡因子为 0,则无需修改,满足AVL树。
2. 如果插入新节点后 parent 的平衡因子为正负1,说明插入前 parent 平衡因子为0,此时以 parent 为根的树高度增加,需要将
parent 的 parent节点继续更新,即向上更新。
3. 如果插入新节点后 parent 的平衡因子为正负2,此时不满足 AVL树的性质,需要进行旋转处理。 这种情况只有第一次为情况2时,向上更新后,parent节点(其实是插入节点的祖父节点)出现的情况。一旦出现该情况之后,旋转之后便无需再继续向上更新,跳出循环更新即可。

该部分代码结构框架如下:
 

while(parent)
{
    if(parent->left==cur)
    {
        --parent->bf;
    }
    else
    {
        ++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)
    {
        //相关操作
        break;
    }
    else
    {
        assert(false);
    }
}

AVL树的旋转

上面已经讲到,当插入新节点后,parent节点的平衡因子变为正负2时,已经不满足AVL树的平衡性,需要我们进行旋转操作。

旋转分为四种情况:左单旋、右单旋、左右双旋和右左双旋。分别对应以下插入情况:



 

左单旋:

 
 

void rotateL(Node* _parent)
	{
		Node* subR = _parent->right;
		Node* subRL = _parent->right->left;

		_parent->right = subRL;        //
		if (subRL)
		{
			subRL->parent = _parent;
		}

		subR->left = _parent;
		Node* pNode = _parent->parent;   //记录parent节点的父节点
		_parent->parent = subR;
		if (_parent == root)   //如果parent为树的根节点,需要修改根节点
		{
			subR->parent = nullptr;
			root = subR;
		}
		else                  //如果不是,则需要将subR插入到 parent的父节点的左边或者右边
		{
			if (pNode->left == _parent)
			{
				pNode->left = subR;
			}
			else
			{
				pNode->right = subR;
			}
			subR->parent = pNode;
		}
		_parent->bf = 0;  //更新平衡因子,都置为0
		subR->bf = 0;
	}

右单旋:

void rotateR(Node* _parent)
	{
		Node* subL = _parent->left;
		Node* subLR = _parent->left->right;

		_parent->left = subLR;
		if (subLR)
		{
			subLR->parent = _parent;
		}
		subL->right = _parent;
		Node* pNode = _parent->parent;
		_parent->parent = subL;
		if (_parent == root)
		{
			subL->parent = nullptr;
			root = subL;
		}
		else
		{
			if (pNode->left == _parent)
			{
				pNode->left = subL;
			}
			else
			{
				pNode->right = subL;
			}
			subL->parent = pNode;
		}
		subL->bf = 0;
		_parent->bf = 0;
	}

左右双旋:

双旋的过程可以用两个单旋来完成,只不过需要修改平衡因子,修改平衡因子又分两种情况:
1. 若旋转之前最底下节点的平衡因子为-1:此时将subL平衡因子修改为0,将parent平衡因子修改为1。

2. 若旋转之前最底下节点平衡因子为1:将subL平衡因子修改为-1,parent平衡因子修改为0。

Node* subL = parent->left;
Node* subLR = parent->left->right;
int _bf = subLR->bf;
rotateL(parent->left);
rotateR(parent);
if (_bf == -1)
{
	parent->bf = 1;
	subL->bf = 0;
}
else if (_bf == 1)
{
	parent->bf = 0;
	subL->bf = -1;
}
else
{
	parent->bf = 0;
	subL->bf = 0;
}

右左双旋:

双旋的过程通过两个单旋来完成,需要修改平衡因子
1. 旋转之前最底下节点平衡因子为-1:此时将subR平衡因子设置为1,parent平衡因子设置为0。

2. 旋转之前最底下节点平衡因子为1:此时将subR平衡因子修改为0,parent平衡因子修改为-1。

Node* subR = parent->right;
Node* subRL = parent->right->left;

int _bf = subRL->bf;
rotateR(parent->right);
rotateL(parent);
if (_bf == -1)
{
	parent->bf = 0;
	subR->bf = 1;
}
else if (_bf == 1)
{
	parent->bf = -1;
	subR->bf = 0;
}
else
{
	parent->bf = 0;
	subR->bf = 0;
}


通过上面的讲解我们可以得到一个完整的插入函数:
 

    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 (cur->kv.first > _kv.first)
			{
				parent = cur;
				cur = cur->left;
			}
			else if (cur->kv.first < _kv.first)
			{
				parent = cur;
				cur = cur->right;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(_kv);
		if (cur->kv.first < parent->kv.first)
		{
			parent->left = cur;
			cur->parent = parent;
		}
		else
		{
			parent->right = cur;
			cur->parent = parent;
		}

		while (parent)
		{
			if (cur == parent->left)
			{
				--parent->bf;
			}
			else
			{
				++parent->bf;
			}

			if (parent->bf == -1 || parent->bf == 1)
			{
				cur = parent;
				parent = parent->parent;
			}
			else if (parent->bf == 0)
			{
				break;
			}
			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) //左右双旋
				{
					Node* subL = parent->left;
					Node* subLR = parent->left->right;

					int _bf = subLR->bf;
					rotateL(parent->left);
					rotateR(parent);
					if (_bf == -1)
					{
						parent->bf = 1;
						subL->bf = 0;
					}
					else if (_bf == 1)
					{
						parent->bf = 0;
						subL->bf = -1;
					}
					else
					{
						parent->bf = 0;
						subL->bf = 0;
					}
				}
				else if (parent->bf == 2 && cur->bf == -1) //右左双旋
				{
					Node* subR = parent->right;
					Node* subRL = parent->right->left;

					int _bf = subRL->bf;
					rotateR(parent->right);
					rotateL(parent);
					if (_bf == -1)
					{
						parent->bf = 0;
						subR->bf = 1;
					}
					else if (_bf == 1)
					{
						parent->bf = -1;
						subR->bf = 0;
					}
					else
					{
						parent->bf = 0;
						subR->bf = 0;
					}
				}
				break;   //旋转结束后无需继续修改。
			}
			else
			{
				assert(false);
			}

		}
	}

AVL树完整实现(插入,旋转,中序遍历,判断是否平衡,求树的高度)

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

template<class K,class V>
struct AVLNode
{
	AVLNode(const pair<K,V>& _kv) 
	:left(nullptr)
	, right(nullptr)
	, parent(nullptr)
	, kv(_kv)
	, bf(0)
	{}
	AVLNode<K,V>* left;
	AVLNode<K, V>* right;
	AVLNode<K, V>* parent;
	pair<K,V> kv;
	int bf;
};

template <class K,class V>
class AVLTree
{
	typedef AVLNode<K, V> Node;
public:
	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 (cur->kv.first > _kv.first)
			{
				parent = cur;
				cur = cur->left;
			}
			else if (cur->kv.first < _kv.first)
			{
				parent = cur;
				cur = cur->right;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(_kv);
		if (cur->kv.first < parent->kv.first)
		{
			parent->left = cur;
			cur->parent = parent;
		}
		else
		{
			parent->right = cur;
			cur->parent = parent;
		}

		while (parent)
		{
			if (cur == parent->left)
			{
				--parent->bf;
			}
			else
			{
				++parent->bf;
			}

			if (parent->bf == -1 || parent->bf == 1)
			{
				cur = parent;
				parent = parent->parent;
			}
			else if (parent->bf == 0)
			{
				break;
			}
			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) //左右双旋
				{
					Node* subL = parent->left;
					Node* subLR = parent->left->right;

					int _bf = subLR->bf;
					rotateL(parent->left);
					rotateR(parent);
					if (_bf == -1)
					{
						parent->bf = 1;
						subL->bf = 0;
					}
					else if (_bf == 1)
					{
						parent->bf = 0;
						subL->bf = -1;
					}
					else
					{
						parent->bf = 0;
						subL->bf = 0;
					}
				}
				else if (parent->bf == 2 && cur->bf == -1) //右左双旋
				{
					Node* subR = parent->right;
					Node* subRL = parent->right->left;

					int _bf = subRL->bf;
					rotateR(parent->right);
					rotateL(parent);
					if (_bf == -1)
					{
						parent->bf = 0;
						subR->bf = 1;
					}
					else if (_bf == 1)
					{
						parent->bf = -1;
						subR->bf = 0;
					}
					else
					{
						parent->bf = 0;
						subR->bf = 0;
					}
				}
				break;
			}
			else
			{
				assert(false);
			}

		}
	}
	void rotateR(Node* _parent)
	{
		Node* subL = _parent->left;
		Node* subLR = _parent->left->right;

		_parent->left = subLR;
		if (subLR)
		{
			subLR->parent = _parent;
		}
		subL->right = _parent;
		Node* pNode = _parent->parent;
		_parent->parent = subL;
		if (_parent == root)
		{
			subL->parent = nullptr;
			root = subL;
		}
		else
		{
			if (pNode->left == _parent)
			{
				pNode->left = subL;
			}
			else
			{
				pNode->right = subL;
			}
			subL->parent = pNode;
		}
		subL->bf = 0;
		_parent->bf = 0;
	}
	void rotateL(Node* _parent)
	{
		Node* subR = _parent->right;
		Node* subRL = _parent->right->left;

		_parent->right = subRL;
		if (subRL)
		{
			subRL->parent = _parent;
		}

		subR->left = _parent;
		Node* pNode = _parent->parent;
		_parent->parent = subR;
		if (_parent == root)
		{
			subR->parent = nullptr;
			root = subR;
		}
		else
		{
			if (pNode->left == _parent)
			{
				pNode->left = subR;
			}
			else
			{
				pNode->right = subR;
			}
			subR->parent = pNode;
		}
		_parent->bf = 0;
		subR->bf = 0;
	}

	void inorder()
	{
		_inorder(root);
		cout << endl;
	}
	bool isbalance()
	{
		return _isbalance(root);
	}
private:
	Node* root = nullptr;

	void _inorder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_inorder(root->left);
		cout << root->kv.first << " ";
		_inorder(root->right);
	}
	int _deep(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		int left = _deep(root->left);
		int right = _deep(root->right);

		return left > right ? left + 1 : right + 1;
	}

	bool _isbalance(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		int leftdeep = _deep(root->left);
		int rightdeep = _deep(root->right);

		int dis = rightdeep - leftdeep;

		if (dis != root->bf)
		{
			cout << "节点平衡因子错误" << endl;
			return false;
		}
		if (dis > 1 || dis < -1)
		{
			return false;
		}
		else
		{
			return _isbalance(root->left) && _isbalance(root->right);
		}

	}
};

int main()
{
	int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	//int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	AVLTree<int, int> tree;

	for (auto e : arr)
	{
		tree.insert(make_pair(e, 0));
		//tree.insert(pair<int, int>(e, 0));
		cout << e << "---" << tree.isbalance() << endl;
	}
	tree.inorder();

	system("pause");
	return 0;
}

AVL树性能分析

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值