AVL树:AVL树的实现

1.AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下。

因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右 子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均 搜索长度。

AVL树具有以下性质:

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

如果一棵二叉搜索树是高度平衡的,如果它有n个结点,其高度可保持在 O(logN),搜索时间复杂度O(logN),它就是AVL树。

2.AVL树节点的定义

pair是将2个数据组合成一组数据,如stl中的map就是将key和value放在一起来保存,或者当一个函数需要返回2个数据的时候,也可以选择pair。

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

	template<class k, class v >
	struct AVLNode
	{
		pair<k, v> _kv;
		AVLNode<k, v>* _left;
		AVLNode<k, v>* _right;
		AVLNode<k, v>* _parent;
        //_pf记录高度差(右子树高度减去左子树高度)
		int _pf;


		AVLNode(const pair<k, v>& key)
			:_kv(key)
			, _left(nullptr)
			, _right(nullptr)
			,_parent(nullptr)
			,_pf(0)
		{}
	};

3.AVL树的插入

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

1. 按照二叉搜索树的方式插入新节点

2. 调整节点的平衡因子

			//cur为新插入节点 prev为cur的双亲节点
            while (prev != nullptr)
			{
				if (cur == prev->_left)
				{
					prev->_pf--;
				}
				else
				{
					prev->_pf++;
				}
				if (prev->_pf == 0)
				{
					break;
				}
				else if (prev->_pf == -1 || prev->_pf == 1)
				{
					cur = prev;
					prev = prev->_parent;
				}
				else if (prev->_pf == 2 || prev->_pf == -2)
				{
                    //进行旋转操作同时调节平衡因子
				}
				else
				{
					assert(false);
				}
            }

 4.AVL树的旋转

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

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

如下:30的右节点一定是比 60小比 30大的节点,所以我们可以让60 的左节点指向30 的右节点,然后让30 来做这棵子树的根。

我们传的是平衡因子绝对值大于2的节点,也就是上图中的60位置的节点指针。 

		void RotetaR(Node* prev)
		{
			Node* left = prev->_left;
			Node* parent = prev->_parent;

			prev->_left = left->_right;
			if (left->_right)
			{
				left->_right->_parent = prev;
			}

			left->_right = prev;
			prev->_parent = left;

			if (parent == nullptr)
			{
				_root = left;
			}
			else
			{
				if (parent->_left == prev)
				{
					parent->_left = left;
				}
				else
				{
					parent->_right = left;
				}
			}
			left->_parent = parent;

			left->_pf = 0;
			prev->_pf = 0;
		}

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

左旋与右旋类似它是让60所在位置来当子树的根节点。

		void RotetaL(Node* prev)
		{
			Node* right = prev->_right;
			Node* parent = prev->_parent;

			prev->_right = right->_left;
			if (right->_left)
			{
				right->_left->_parent = prev;
			}

			right->_left = prev;
			prev->_parent = right;

			if (parent == nullptr)
			{
				_root = right;
			}
			else
			{
				if (parent->_left == prev)
				{
					parent->_left = right;
				}
				else
				{
					parent->_right = right;
				}

			}
			right->_parent = parent;

			right->_pf = 0;
			prev->_pf = 0;
		}

3. 新节点插入较高左子树的右侧---左右:先左单旋再右单旋

将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再 考虑平衡因子的更新。

		void RotetaLR(Node* prev)
		{
			Node* subL = prev->_left;
			Node* subLR = subL->_right;
			Node* sub = nullptr;
			if (subLR->_pf == 0)
			{
				sub = subLR;
			}
			else if (subLR->_pf == 1)
			{
				sub = subL;
			}
			else
			{
				sub = prev;
			}
			RotetaL(subL);
			RotetaR(prev);
			if (sub == prev)
			{
				sub->_pf = 1;
			}
			else if (sub == subL)
			{
				sub->_pf = -1;
			}

		}

4. 新节点插入较高右子树的左侧---右左:先右单旋再左单旋

与3思路类似:

		void RotetaRL(Node* prev)
		{
			Node* sub = nullptr;
			Node* subR = prev->_right;
			Node* subRL = subR->_left;
			if (subRL->_pf == 0)
			{
				sub = subRL;
			}
			else if (subRL->_pf == 1)
			{
				sub = prev;
			}
			else
			{
				sub = subR;
			}

			RotetaR(prev->_right);

			RotetaL(prev);

			if (sub == prev)
			{
				sub->_pf = -1;
			}
			else if (sub == subR)
			{
				sub->_pf = 1;
			}
		}

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

1. prev的平衡因子为2,说明prev的右子树高,设prev的右子树的根为right

  • 当right的平衡因子为1时,执行左单旋
  • 当right的平衡因子为-1时,执行右左双旋

2. prev的平衡因子为-2,说明prev的左子树高,设prev的左子树的根为left

  • 当left的平衡因子为-1是,执行右单旋
  • 当left的平衡因子为1时,执行左右双旋

旋转完成后,原prev为根的子树个高度降低,已经平衡,不需要再向上更新。

5.AVL树的优劣

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这 样可以保证查询时高效的时间复杂度,即(logN)。

但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时, 有可能一直要让旋转持续到根的位置。

因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

感谢大当家的观看!

由于时间原因AVL树的删除并没有实现,用的是搜索二叉树的erase。

大家感兴趣的可以自己去实现一下。

因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不 错与删除不同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。

#include<iostream>
#include<cassert>

using namespace std;

namespace bit
{
	template<class k, class v >
	struct AVLNode
	{
		pair<k, v> _kv;
		AVLNode<k, v>* _left;
		AVLNode<k, v>* _right;
		AVLNode<k, v>* _parent;
		int _pf;


		AVLNode(const pair<k, v>& key)
			:_kv(key)
			, _left(nullptr)
			, _right(nullptr)
			,_parent(nullptr)
			,_pf(0)
		{}
	};


	template<class k, class v = int>
	class AVL
	{
		typedef AVLNode<k, v> Node;
	public:
		AVL() = default;
		AVL(const AVL<k, v>& root)
		{
			_root = Copy(root._root);
		}

		~AVL()
		{
			Destroy(_root);
			_root = nullptr;
		}

		bool Insert(const k& ret)
		{
			if (_root == nullptr)
			{
				_root = new Node({ ret,0 });
				return true;
			}
			Node* prev = nullptr;
			Node* root = _root;
			while (root != nullptr)
			{
				if (root->_kv.first > ret)
				{
					prev = root;
					root = root->_left;
				}
				else if (root->_kv.first < ret)
				{
					prev = root;
					root = root->_right;
				}
				else return false;
			}
			Node* cur = new Node({ ret,0 });
			if (prev->_kv.first > ret)
			{
				prev->_left = cur;
				cur->_parent = prev;
			}
			else
			{
				prev->_right = cur;
				cur->_parent = prev;
			}

			while (prev != nullptr)
			{
				if (cur == prev->_left)
				{
					prev->_pf--;
				}
				else
				{
					prev->_pf++;
				}
				if (prev->_pf == 0)
				{
					break;
				}
				else if (prev->_pf == -1 || prev->_pf == 1)
				{
					cur = prev;
					prev = prev->_parent;
				}
				else if (prev->_pf == 2 || prev->_pf == -2)
				{
					if (cur == prev->_right)
					{
						if (cur->_pf == 1)
						{
							//左单旋
							RotetaL(prev);
						}
						else
						{
							//右左旋
							RotetaRL(prev);
						}
					}
					if (cur == prev->_left)
					{
						if (cur->_pf == -1)
						{
							//右单选旋
							RotetaR(prev);
						}
						else
						{
							//左右旋
							RotetaLR(prev);
						}
					}
					break;
				}
				else
				{
					assert(false);
				}
			}
			return true;
		}

		bool Find(const k& ret)
		{
			Node* root = _root;
			while (root != nullptr)
			{
				if (root->_kv.first > ret)
				{
					root = root->_left;
				}
				else if (root->_kv.first < ret)
				{
					root = root->_right;
				}
				else return true;
			}
			return false;
		}

		void Inorder()
		{
			inorder(_root);
			cout << endl;
		}



		bool Erase(const k& ret)
		{
			Node* prev = nullptr;
			Node* root = _root;
			while (root != nullptr)
			{
				if (ret > root->_kv.first)
				{
					prev = root;
					root = root->_right;
				}
				else if (ret < root->_kv.first)
				{
					prev = root;
					root = root->_left;
				}
				else
				{
					if (root->_left == nullptr)
					{
						if (prev == nullptr)
						{
							_root = root->_right;
							if(_root)
							_root->_parent = nullptr;
						}
						else if (prev->_left == root)
						{
							prev->_left = root->_right;
							if (root->_right)
							{
								root->_right->_parent = prev;
							}
						}
						else if (prev->_right == root)
						{
							prev->_right = root->_right;
							if (root->_right)
							{
								root->_right->_parent = prev;
							}
						}
						delete root;
						return true;
					}
					else if (root->_right == nullptr)
					{
						if (prev == nullptr)
						{
							_root = root->_left;
							if(_root)
							_root->_parent = nullptr;
						}
						else if (prev->_left == root)
						{
							prev->_left = root->_left;
							if (root->_left)
							{
								root->_left->_parent = prev;
							}
						}
						else if (prev->_right == root)
						{
							prev->_right = root->_left;
							if (root->_left)
							{
								root->_left->_parent = prev;
							}
						}
						delete root;
						return true;
					}
					else//左右节点都不为空找右子树最小节点||左子树最大节点
					{
						Node* right = root->_right;
						prev = root;
						while (right->_left != nullptr)
						{
							prev = right;
							right = right->_left;
						}
						root->_kv.first = right->_kv.first;
						root->_kv.second = right->_kv.second;
						if (prev == root)
						{
							prev->_right = right->_right;
						}
						else
						{
							prev->_left = right->_right;
						}
						delete right;
						return true;
					}
				}
			}
			return false;
		}

		int _Height()
		{
			return height(_root);
		}


	private:
		int height(Node* root)
		{
			if (root == nullptr) return 0;
			int l = height(root->_left);
			int r = height(root->_right);
			return l > r ? l + 1 : r + 1;
		}
		void RotetaL(Node* prev)
		{
			Node* right = prev->_right;
			Node* parent = prev->_parent;

			prev->_right = right->_left;
			if (right->_left)
			{
				right->_left->_parent = prev;
			}

			right->_left = prev;
			prev->_parent = right;

			if (parent == nullptr)
			{
				_root = right;
			}
			else
			{
				if (parent->_left == prev)
				{
					parent->_left = right;
				}
				else
				{
					parent->_right = right;
				}

			}
			right->_parent = parent;

			right->_pf = 0;
			prev->_pf = 0;
		}

		void RotetaR(Node* prev)
		{
			Node* left = prev->_left;
			Node* parent = prev->_parent;

			prev->_left = left->_right;
			if (left->_right)
			{
				left->_right->_parent = prev;
			}

			left->_right = prev;
			prev->_parent = left;

			if (parent == nullptr)
			{
				_root = left;
			}
			else
			{
				if (parent->_left == prev)
				{
					parent->_left = left;
				}
				else
				{
					parent->_right = left;
				}
			}
			left->_parent = parent;

			left->_pf = 0;
			prev->_pf = 0;
		}

		void RotetaLR(Node* prev)
		{
			Node* subL = prev->_left;
			Node* subLR = subL->_right;
			Node* sub = nullptr;
			if (subLR->_pf == 0)
			{
				sub = subLR;
			}
			else if (subLR->_pf == 1)
			{
				sub = subL;
			}
			else
			{
				sub = prev;
			}
			RotetaL(subL);
			RotetaR(prev);
			if (sub == prev)
			{
				sub->_pf = 1;
			}
			else if (sub == subL)
			{
				sub->_pf = -1;
			}

		}

		void RotetaRL(Node* prev)
		{
			Node* sub = nullptr;
			Node* subR = prev->_right;
			Node* subRL = subR->_left;
			if (subRL->_pf == 0)
			{
				sub = subRL;
			}
			else if (subRL->_pf == 1)
			{
				sub = prev;
			}
			else
			{
				sub = subR;
			}

			RotetaR(prev->_right);

			RotetaL(prev);

			if (sub == prev)
			{
				sub->_pf = -1;
			}
			else if (sub == subR)
			{
				sub->_pf = 1;
			}
		}
		void inorder(Node* root)
		{
			if (root == nullptr) return;

			inorder(root->_left);
			cout << root->_kv.first << ' ';
			inorder(root->_right);
		}

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

			Node* val = new Node(root->_kv);
			val->_left = Copy(root->_left);
			val->_right = Copy(root->_right);
			return val;
		}

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

			Destroy(root->_left);
			Destroy(root->_right);
			delete root;
		}

		Node* _root = nullptr;
	};
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值