数据结构-AVL树(平衡二叉树)

目录

AVL树的概念及结构

概念

结构

AVL树的插入

右旋转

左旋转

左右双旋

右左旋转 

AVL树的删除 

插入与删除的总结 

AVL树的基本操作

构造

析构

= 重载

[ ] 重载

中序遍历

判断一棵树是否为AVL树

代码


AVL树的概念及结构

概念

   对于普通二叉搜索树,如果在数据有序或者接近有序时插入删除,二叉搜索树会退化成单支树,也就是类似单链表的结构;因此为了避免普通二叉搜索树的高度增长过快,降低二叉搜索树的效率与性能。

   规定在插入和删除二叉搜索树的结点时,要保证任意结点的左右子树高度差的绝对值不超过1,我们将这样特殊的二叉搜索树称为AVL树(平衡二叉树)。每一个结点中有记录其左右子树高度差的平衡因子,也即平衡因子的值只可能是-1,0,1。(1如果某结点的左子树高度比右子树高度高一则平衡因子为-1,如果某结点的左子树高度比右子树高度低一则平衡因子为1,如果某结点的左子树高度与右子树高度一样则平衡因子为0)。6e47882cdb75476f918055a6f1ec1220.png

   AVL树有以下性质:

1,每个结点的左右子树都是AVL树

2,左子树与右子树的高度差只能为-1,0,1(规定平衡因子等于右子树的高度减左子树的高度)

3,如果AVL树非常平衡,则整体结构接近满二叉树,所以当结点为N个时,其高度为log(2)N

结构

   为了便于更新平衡因子与旋转处理AVL树的链式结构采用三叉链,也就是AVL树的每个结点都包含:双亲结点地址,左孩子地址,右孩子地址,平衡因子,KV数据。6224ef42109345d6af710afc9780fd64.png

AVL树的插入

第一步:按照二叉搜索树的插入操作进行新结点的插入,但需要注意的是要将新插入结点中的_parent指向改为其双亲。d6a878894c824c22a45888a72c5f64a7.png

第二步:更新平衡因子,从插入结点的双亲开始往上更新;如果插入的结点是双亲的右孩子则双亲的平衡因子加一,如果插入的结点是双亲的左孩子则双亲的平衡因子减一;更新过的平衡因子有三种情况:

1,如果平衡因子变为0,则说明原来的平衡因子为-1或者1,也就是原来左右子树的高度差绝对值为1,经过插入新结点,使该结点的左右子树达到平衡,所以无须继续往上更新平衡因子。

2,如果平衡因子变为-1或者1,则说明原来的平衡因子为0,也就是原来左右子树的高度一致,经过插入新结点,使该结点的左右子树的高度差绝对值变为1,所以需要继续往上更新平衡因子。

3,如果平衡因子变为-2或者2,则说明原来的平衡因子为-1或者1,也就是原来左右子树高度差绝对值为1,经过插入新结点,使该结点的左右子树高度差绝对值为2,造成不满足AVL树性质要求需要进行旋转处理。71ed694dc3da4fa39bb05ac3e410f828.png

第三步:旋转处理,使不平衡的AVL树变为平衡的AVL树;造成平衡的AVL树变为不平衡的AVL树的原因有4种,也就是说使不平衡的AVL树变为平衡的AVL树的旋转处理方式也有4种,每一种原因对应一种旋转处理方式。

右旋转

   在A结点的左孩子B的左子树上插入新结点,导致以A为根的子树失去平衡(左子树比右子树高),需要进行右旋转处理c8866d33b36346dd895780f6e5a34e61.png

右旋转的具体操作:先让B结点的_right指向A结点,再让A结点的_left指向原B结点的右子树b。57ba98a0e3514890ba0ff218b3cab7fb.png

5746e220e2974c7999a8de1b16dfa96a.png

左旋转

   在A结点的右孩子B的右子树上插入新结点,导致以A为根的子树失去平衡(左子树比右子树低),需要进行左旋转处理。4d78627a477a441ca960076df3cd5d0c.png

左旋转的具体操作:先让B结点的_left指向A结点,再让A结点的_right指向原B结点的左子树b。4e9057a686884ac38da0831dee45018d.png

149071fff2894e62ad5e1e9aee208cd7.png

左右双旋

   由于在A的左孩子B的右子树上插入新结点,A的平衡因子由-1变为-2,导致以A为根的子树失去平衡,需要进行两次旋转操作,先左旋转再右旋转。左旋转是以B结点为轴,右旋转是以A结点为轴。5fd064be38ad4c6980e492cc8a72e4ae.png

具体操作:先将A结点的左孩子B的右子树的根节点C向左上旋转提升到B结点的位置,然后把该C结点向右上旋转提升到A结点的位置。93e1ab7e716c4bc2b9e3750524e98077.png

// 左右旋
	void RotateLR(Node* parrnt)
	{
		Node* subL = parrnt->_left;
		Node* subLR = subL->_right;
		short bf = subLR->_bf;

		RotateL(parrnt->_left);
		RotateR(parrnt);

		if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parrnt->_bf = 0;
		}
		else if (bf == -1)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parrnt->_bf = 1;
		}
		else if(bf==0)
		{

			subLR->_bf = 0;
			subL->_bf = 0;
			parrnt->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

右左旋转 

   由于在A的右孩子B的左子树上插入新结点,A的平衡因子由1变为2,导致以A为根的子树失去平衡,需要进行两次旋转操作,先右旋转在左旋转。右旋转是以B结点为轴,左旋转是以A结点为轴。8ca2556db2b14b21ad8587483c7b5e7a.png

具体操作:先将A结点的右孩子B的左子树的根节点C向右上旋转提升到B结点的位置,然后把该C结点向左上旋转提升到A结点的位置。38494f6cc8c24369aa8247f45deb94a7.png

// 右左旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		short bf = subRL->_bf;

		RotateR(parent->_right);
		RotateL(parent);

		if (bf == 1)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf==-1)
		{
			subRL->_bf = 0;
			subR->_bf = 1;
			parent->_bf = 0;
		}
		else if(bf==0)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

AVL树的删除 

第一步:先找到要删除的结点78140275f2144466b126fb504fcd582d.png

第二步:按照要删除结点的类型进行分类删除:1,左子树为空   2,右子树为空   3,左右子树都为空   4,左右子树都不为空0f85573ac6e54a6c823f326974dfcf96.png

第三步:更新平衡因子。如果删除的结点在其双亲的左边则双亲的平衡因子++,如果删除的结点在其双亲结点的右边则双亲的平衡因子--。

1,如果更新后parent->_bf==0,说明parent->_bf原来是-1或者1,把高的那边删除掉了,高度变小了,继续往上更新。

2,如果更新后parent->_bf==-1或者parenr->_bf==1,说明parent->_bf原来是0,删除了其左右结点其中的一个,高度没有变,不需要继续往上更新。

3,如果更新后parent->_bf==-2或者parenr->_bf==2,说明AVL树不平衡,需要旋转处理。

第四步:旋转处理。

// 旋转规则(有四种情况):

// firstDepth是parent高度最高的孩子结点,secondDepth是firstDepth高度最后的结点
// 1,如果first是parent的左孩子,second是first的左孩子---右旋转
// 2,如果first是parent的左孩子,second是first的右孩子---左右双旋

// 3,如果first是parent的右孩子,second是first的右孩子---左旋转

// 4,如果first是parent的右孩子,second是first的左孩子---右左双旋

86a5e39870b64ee7a155cfd8da299ea3.png

8d82e9aeb5264073b720d3cd74ac4d25.png

插入与删除的总结 

9b376f7e76b940e9bb904c6f48b3cdde.png

AVL树的基本操作

构造

// 无参构造
AVLTree()
		:_root(nullptr)
	{

	}
// 拷贝构造
	Node* _CopyAVLTree(Node* root, Node* parent)
	{
		if (root == nullptr)
		{
			return nullptr;
		}

		Node* newNode = new Node(root->_kv);
		newNode->_bf = root->_bf;
		newNode->_parent = parent;
		newNode->_left = _CopyAVLTree(root->_left,newNode);
		newNode->_right = _CopyAVLTree(root->_right,newNode);

		return newNode;
	}

AVLTree(const AVLTree& t)
	{
		_root=_CopyAVLTree(t._root, nullptr);
	}

析构

//析构
~AVLTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}

//销毁
	void _Destroy(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}

= 重载

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

[ ] 重载

V& operator[](const K& key)
	{
		pair<Node*, bool> ret = Insert(make_pair(key,V()));
		return (ret.first)->_kv.second;
	}

中序遍历

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

e6f272e3b9c64604b8e22454508728fb.png

判断一棵树是否为AVL树

bool IsAVLTree()
	{
		return _IsAVLTree(_root);
	}

99b4290d2dda4f7ab36487340742b263.png

代码

// .hpp
#pragma once
#include <iostream>
#include <cassert>
using namespace std;

template <class K,class V>
struct AVLTreeNode
{
	short _bf; // 平衡因子---记录其左右子树的高度差(右子树高度减左子树高度)

	struct AVLTreeNode<K, V>* _parent; // 记录其双亲的地址
	struct AVLTreeNode<K, V>* _left; // 记录其左孩子的地址
	struct AVLTreeNode<K, V>* _right; // 记录其右孩子的地址

	pair<K, V> _kv; // kv值

	AVLTreeNode(const pair<K,V>& kv)
		:_bf(0)
		,_parent(nullptr)
		,_left(nullptr)
		,_right(nullptr)
		,_kv(kv)
	{

	}
};

template <class K,class V>
class AVLTree
{
	typedef struct AVLTreeNode<K, V> Node;
private:
	Node* _root;

	// 左旋转---左子树低
	void RotateL(Node* parent) // parent是平衡因子为-2或者2的结点
	{
		Node* parentParent = parent->_parent; // 记录其双亲
		Node* subR = parent->_right; // 记录其右孩子
		Node* subRL = subR->_left; // 记录其右孩子的左孩子
		
		// 让subR的左孩子指向parent,parent的双亲指向subR
		subR->_left = parent;
		parent->_parent = subR;

		// 让parent的右孩子指向subR的左孩子
		parent->_right = subRL;
		if (subRL) // 如果subRL不为空,则让subR的左孩子的_parent指向parent
		{
			subRL->_parent = parent;
		}

		// 更新parent的双亲结点中的左孩子或者右孩子指向,并更新subR的_parent的指向
		if (parent == _root)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = subR;
			}
			else if(parentParent->_right==parent)
			{
				parentParent->_right = subR;
			}
			subR->_parent = parentParent;
		}

		// 更新旋转处理后的平衡因子
		parent->_bf = 0;
		subR->_bf = 0;
	}

	// 右旋转
	void RotateR(Node* parent) // parent是平衡因子为-2或者2的结点
	{
		Node* parentParent = parent->_parent; // 记录其双亲
		Node* subL = parent->_left; // 记录其左孩子
		Node* subLR = subL->_right; // 记录其左孩子的右孩子

		// parent的双亲指向subL,subL的右孩子指向parent
		parent->_parent = subL;
		subL->_right = parent;

		// parent的左孩子指向subL的右孩子
		parent->_left = subLR;
		if (subLR) // 如果subLR不为空,则让subLR的双亲指向parent
		{
			subLR->_parent = parent;
		}

		if (_root == parent) // parent的双亲为空则说明parent是整棵树的根节点
		{
			_root = subL; // 让subL当整棵树的根节点
			subL->_parent = nullptr; // 并让subL的双亲指向空
		}
		else // 否则parent是某棵子树的根节点,更新parentParent左孩子或者右孩子的指向
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = subL;
			}
			else
			{
				parentParent->_right = subL;
			}
			subL->_parent = parentParent; // 更新subL双亲的指向
		}

		parent->_bf = subL->_bf = 0; // 经过旋转更新平衡因子
	}

	// 左右旋
	void RotateLR(Node* parrnt)
	{
		Node* subL = parrnt->_left;
		Node* subLR = subL->_right;
		short bf = subLR->_bf;

		RotateL(parrnt->_left);
		RotateR(parrnt);

		if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parrnt->_bf = 0;
		}
		else if (bf == -1)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parrnt->_bf = 1;
		}
		else if(bf==0)
		{

			subLR->_bf = 0;
			subL->_bf = 0;
			parrnt->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	// 右左旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		short bf = subRL->_bf;

		RotateR(parent->_right);
		RotateL(parent);

		if (bf == 1)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf==-1)
		{
			subRL->_bf = 0;
			subR->_bf = 1;
			parent->_bf = 0;
		}
		else if(bf==0)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	// parent为删除结点的双亲,child为删除的结点
	void UpdateBF(Node* parent,Node* child)
	{
		while (parent != nullptr)
		{
			if (parent->_left == child)
			{
				parent->_bf++;
			}
			else if (parent->_right == child)
			{
				parent->_bf--;
			}

			// 检查平衡因子是否异常
			if (parent->_bf == 0)
			{
				child = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				break;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				// 求出平衡因子异常结点的左右子树中高的那一棵子树
				Node* firstDepth = parent->_left;
				int maxDepth = AVLTreeDepth(parent->_left);
				int minDepth = AVLTreeDepth(parent->_right);
				if (maxDepth < minDepth)
				{
					firstDepth = parent->_right;
				}

				// 求出firstDepth结点的左右子树中高的那一棵子树
				Node* secondDepth = firstDepth->_left;
				maxDepth = AVLTreeDepth(firstDepth->_left);
				minDepth = AVLTreeDepth(firstDepth->_right);
				if (maxDepth < minDepth)
				{
					secondDepth = firstDepth->_right;
				}

				// 旋转规则(有四种情况):
				// firstDepth是parent高度最高的孩子结点,secondDepth是firstDepth高度最后的结点
				// 1,如果first是parent的左孩子,second是first的左孩子---右旋转
				// 2,如果first是parent的左孩子,second是first的右孩子---左右双旋
				// 3,如果first是parent的右孩子,second是first的右孩子---左旋转
				// 4,如果first是parent的右孩子,second是first的左孩子---右左双旋
				if (parent->_left == firstDepth)
				{
					if (firstDepth->_left == secondDepth)
					{
						RotateR(parent);
					}
					else if (firstDepth->_right == secondDepth)
					{
						RotateLR(parent);
					}
				}
				else if (parent->_right == firstDepth)
				{
					if (firstDepth->_left == secondDepth)
					{
						RotateRL(parent);
					}
					else if (firstDepth->_right == secondDepth)
					{
						RotateL(parent);
					}
				}
				break;
			}
			else
			{
				assert(false);
			}
		}
	}

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

		_InOrder(root->_left);

		cout << " K:" << root->_kv.first << " V:" << root->_kv.second << " bf:" << root->_bf <<endl;

		_InOrder(root->_right);
	}

	// 判断是否为AVL树
	bool _IsAVLTree(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}

		int left = AVLTreeDepth(root->_left);
		int right = AVLTreeDepth(root->_right);

		return abs(left - right) < 2 && _IsAVLTree(root->_left) && _IsAVLTree(root->_right);
	}

	// 拷贝构造
	Node* _CopyAVLTree(Node* root, Node* parent)
	{
		if (root == nullptr)
		{
			return nullptr;
		}

		Node* newNode = new Node(root->_kv);
		newNode->_bf = root->_bf;
		newNode->_parent = parent;
		newNode->_left = _CopyAVLTree(root->_left,newNode);
		newNode->_right = _CopyAVLTree(root->_right,newNode);

		return newNode;
	}

	//销毁
	void _Destroy(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}

public:
	AVLTree()
		:_root(nullptr)
	{

	}

	AVLTree(const AVLTree& t)
	{
		_root=_CopyAVLTree(t._root, nullptr);
	}

	~AVLTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}

	V& operator[](const K& key)
	{
		pair<Node*, bool> ret = Insert(make_pair(key,V()));
		return (ret.first)->_kv.second;
	}

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

	pair<Node*,bool> Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr) // 若AVL为空树,则直接插入
		{
			_root = new Node(kv);
			return make_pair(_root,true);
		}

		Node* prev = nullptr; // 记录插入结点所在路径的最后一个结点
		Node* curr = _root; // 引导prev走到最后一个结点

		// 寻找插入位置
		while (curr)
		{ 
			if (curr->_kv.first > kv.first) // 要插入的值比较小,往左走
			{
				prev = curr;
				curr = curr->_left;
			}
			else if (curr->_kv.first < kv.first) // 要插入的值比根结点的值大,往右走
			{
				prev = curr;
				curr = curr->_right;
			}
			else // 要插入的值比根结点的值大,则插入失败
			{
				return make_pair(curr,false);
			}
		}

		// 开始插入
		curr = new Node(kv);
		Node* ret = curr;
		if (prev->_kv.first > kv.first) // 左插入 
		{
			prev->_left = curr;
			curr->_parent = prev; // 
		}
		else if (prev->_kv.first < kv.first) // 右插入
		{
			prev->_right = curr;
			curr->_parent = prev; // 
		}

		// 更新平衡因子---将新插入结点curr的祖先的平衡因子更新
		// while(curr!=_root)
		while (prev != nullptr)
		{
			if (prev->_left == curr)
			{
				prev->_bf--; // 如果是往其左子树插入,则_bf--;
			}
			else if (prev->_right == curr)
			{
				prev->_bf++; // 如果是往其右子树插入,则_bf++;
			}

			if (prev->_bf == 0) // 左右子树达到平衡,不需要继续往上更新平衡因子
			{
				break;
			}
			else if (prev->_bf == 1 || prev->_bf == -1) // 需要继续往上更新平衡因子
			{
				curr = prev;
				prev = prev->_parent;
			}
			else if (prev->_bf == 2 || prev->_bf == -2) // 左右子树不平衡,需要继续旋转调整
			{
				if (prev->_bf == 2)
				{
					if (curr->_bf == 1) // 右子树高---右结点的右子树上插入
					{
						RotateL(prev); // 左旋转
					}
					else if (curr->_bf == -1) // 右子树高---右结点的左子树上插入
					{
						RotateRL(prev); // 先右旋再左旋
					}
				}
				else
				{
					if (curr->_bf == 1) // 左子树高---左结点的右子树上插入
					{
						RotateLR(prev); // 先左旋再右旋
					}
					else if (curr->_bf == -1) // 左子树高---左结点的左子树上插入
					{
						RotateR(prev);
					}
				}
				break;
			}
			else
			{
				assert(false);
			}
		}
		return make_pair(ret, true);
	}

	bool Erase(const pair<K, V>& kv)
	{
		if (_root == nullptr) // 如果AVL树为空则不需要删除
		{
			return false;
		}

		Node* prev = nullptr; // 记录删除结点的双亲
		Node* curr = _root; // 记录删除的结点

		while (curr) // 寻找要删除的结点
		{
			if (curr->_kv.first > kv.first) // 往左寻找
			{
				prev = curr;
				curr = curr->_left;
			}
			else if (curr->_kv.first < kv.first) // 往右寻找
			{
				prev = curr;
				curr = curr->_right;
			}
			else // 找到了---开始删除
			{
				if (curr->_left == nullptr) // 删除结点的左子树为空
				{
					if (prev == nullptr) // 删除根结点
					{
						_root = curr->_right;
						if (curr->_right)
						{
							curr->_right->_parent = nullptr;
						}		
					}
					else // 删除非根结点
					{
						UpdateBF(prev, curr); // 删除之前调整平衡因子

						if (prev->_left == curr)
						{
							prev->_left = curr->_right;
							
						}
						else
						{
							prev->_right = curr->_right;
							
						}

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

					delete curr;
					return true;
				}
				else if (curr->_right == nullptr) // 删除结点的右子树为空
				{
					if (prev == nullptr) // 删除根结点,更新_root
					{
						_root = curr->_left;
						if (curr->_left)
						{
							curr->_left->_parent = nullptr;
						}	
					}
					else // 删除非根结点
					{
						UpdateBF(prev, curr);
						if (prev->_left == curr)
						{
							prev->_left = curr->_left;
							
						}
						else
						{
							prev->_right = curr->_left;
							
						}

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

					delete curr;
					return true;
				}
				else // 删除结点的左右子树都不为空
				{
					Node* min = curr->_right;
					while (min->_left)
					{
						min = min->_left;
					}
					pair<K, V> tmp = min->_kv;
					Erase(tmp); // 替换删除
					curr->_kv = tmp;
					return true;
				}
			}
		}
		return false;
	}

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

	Node* Find(const K& key)
	{
		Node* curr = _root;
		while (curr)
		{
			if (curr->_kv.first > key)
			{
				curr = curr->_left;
			}
			else if (curr->_kv.first < key)
			{
				curr = curr->_right;
			}
			else
			{
				return curr;
			}
		}
		return nullptr;
	}

	int AVLTreeDepth(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}

		int leftDepth = AVLTreeDepth(root->_left);
		int rightDepth = AVLTreeDepth(root->_right);

		return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1;
	}

	bool IsAVLTree()
	{
		return _IsAVLTree(_root);
	}
};

 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值