AVLTree(二叉平衡树)底层实现

Posted Y—X

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AVLTree(二叉平衡树)底层实现相关的知识,希望对你有一定的参考价值。

1. AVL树的概念

如果二叉搜索树的插入序列是有序的或者是接近有序,那么二叉搜索树就会退化为单支树(类似单链表),查找元素相当于在顺序表中搜索元素,时间复杂度为O(N)。

AVLtree(Adelson Velskii Landis tree)是一个加上额外平衡条件的二叉搜索树,左右子树高度之差(简称平衡因子)的绝对值不超过1,如果它有n个节点,高度可保持在O(logn),搜索时间复杂度为O(logn)
在这里插入图片描述

1.1 AVL树节点的定义

template <class K, class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	pair<K, V> _kv;

	int _bf;//平衡因子 左右高度差
	
	AVLTreeNode(const pair<K, V>& kv)//构造
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0)
	{}
};

1.2 AVL树的插入

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

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

  • 如果插入到左侧,只需要将平衡因子-1
  • 如果插入到右侧,只需要将平衡因子+1

2.插入后调整节点的平衡因子

  • 此时平衡因子有三种情况:
  1. 平衡因子为0,插入之前平衡因子为正负1,插入后被调整为0,此时满足AVL树的性质,插入成功
  2. 平衡因子为正负1,插入之前平衡因子为0,插入之后需要继续向上更新
  3. 平衡因子为正负2,则违反了AVL树的性质,需要对其进行旋转处理

1.3AVL树的旋转处理

1.3.1右单旋

新节点插入较高左子树的左侧—左左:右单旋
在这里插入图片描述

//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;//此时parent->bf=-2,左边高
		Node* subLR = subL->_right;

		//将subLR链接到parent的左侧
		parent->_left = subLR;
		if (subLR != nullptr)
		{
			subLR->_parent = parent;//修改自己的parent
		}
		
		Node* pParent = parent->_parent;//保存一份

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

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

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

1.3.2左单旋

新节点插入较高右子树的右侧—右右:左单旋
在这里插入图片描述

//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		
		//subRL连接到parent
		parent->_right = subRL;
		if(subRL)
			subRL->_parent = parent;//NULL   BUg

		Node* pParent = parent->_parent;//保存一份来连接

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

		if (pParent == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else // 不为根
		{
			if (pParent->_left == parent)//parent在上一层的左侧
			{
				pParent->_left = subR;
			}
			else
			{
				pParent->_right = subR;
			}
			subR->_parent = pParent; 
		}

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

1.3.3 左右双旋

新节点插入较高左子树的右侧—左右:先左单旋再右单旋
在这里插入图片描述

void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = parent->_left->_right;
		int bf = subLR->_bf;

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

		if (bf == 1)//说明是subLR是右树插入
		{
			subLR->_bf = 0;
			parent->_bf = 0;
			subL->_bf = -1;
		}
		else if (bf == -1)//说明是subLR是左树插入
		{
			subLR->_bf = 0;
			parent->_bf = 1;
			subL->_bf = 0;
		}
		else if (bf == 0)
		{
			subLR->_bf = subL->_bf = parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

1.3.4 右左双旋

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

在这里插入图片描述

void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		int bf = subRL->_bf;

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

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

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

  1. Parent的平衡因子为2,说明Parent的右子树高,设Parent的右子树的根为SubR
    • 当SubR的平衡因子为1时,执行左单旋
    • 当SubR的平衡因子为-1时,执行右左双旋
  2. Parent的平衡因子为-2,说明Parent的左子树高,设Parent的左子树的根为SubL
    • 当SubL的平衡因子为-1是,执行右单旋
    • 当SubL的平衡因子为1时,执行左右双旋

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

1.4完整代码实现及验证

#pragma once

#include <iostream>
#include <vector>
#include <assert.h>
#include <math.h>

using namespace std;

template <class K, class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	pair<K, V> _kv;

	int _bf;//平衡因子 左右高度差
	
	AVLTreeNode(const pair<K, V>& kv)//构造
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0)
	{}
};

template<class K, class V>
struct AVLTree
{
	typedef struct AVLTreeNode<K, V> Node;
public:
	AVLTree() = default;
	AVLTree(const AVLTree<K, V>& t);
	AVLTree<K, V>& operator=(AVLTree<K, V> t);
	//~AVLTree();

	//插入节点
	pair<Node*, bool> Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return make_pair(_root, true);
		}

		//有根了,按照平衡二叉树的方法进行插入
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)//K值比较,小于往左边走
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(cur, false);//相等,返回已有元素的指针
			}
		}

		//找到插入的位置了,判断插入左边还是右边
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		//1.更新平衡因子
		//新增节点会影响它到这条节点路径上的祖先
		Node* newNode = cur;//提前保存cur
		while (parent)
		{
			if (cur == parent->_right)//cur在parent左侧
			{
				parent->_bf++;
			}
			else//cur在parent的右侧
			{
				parent->_bf--;
			}

			if (parent->_bf == 0)//调节后parent的_bf为0,说明这棵树已经平衡
			{
				break;
			}
			else if(abs(parent->_bf) == 1)//继续往上走
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (abs(parent->_bf) == 2)//不平衡了 旋转
			{
				if (parent->_bf == -2)
				{
					if (cur->_bf == -1)//右旋
					{
						RotateR(parent);
					}
					else // cur->_bf == 1
					{
						RotateLR(parent);
					}
				}
				else // parent->_bf == 2
				{
					if (cur->_bf == 1)
					{
						RotateL(parent);//左旋
					}
					else // cur->_bf == -1
					{
						RotateRL(parent);
					}
				}

				break;
			}
			else
			{
				assert(false);//不可能走到这一步,走到这里说明发生了逻辑错误
			}
		}

		return make_pair(newNode, true);
	}

	//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;//此时parent->bf=-2,左边高
		Node* subLR = subL->_right;

		//将subLR链接到parent的左侧
		parent->_left = subLR;
		if (subLR != nullptr)
		{
			subLR->_parent = parent;//修改自己的parent
		}
		
		Node* pParent = parent->_parent;//保存一份

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

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

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

	//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		
		//subRL连接到parent
		parent->_right = subRL;
		if(subRL)
			subRL->_parent = parent;//NULL   BUg

		Node* pParent = parent->_parent;//保存一份来连接

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

		if (pParent == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else // 不为根
		{
			if (pParent->_left == parent)//parent在上一层的左侧
			{
				pParent->_left = subR;
			}
			else
			{
				pParent->_right = subR;
			}
			subR->_parent = pParent; 
		}

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

	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = parent->_left->_right;
		int bf = subLR->_bf;

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

		if (bf == 1)//说明是subLR是右树插入
		{
			subLR->_bf = 0;
			parent->_bf = 0;
			subL->_bf = -1;
		}
		else if (bf == -1)//说明是subLR是左树插入
		{
			subLR->_bf = 0;
			parent->_bf = 1;
			subL->_bf = 0;
		}
		else if (bf == 0)
		{
			subLR->_bf = subL->_bf = parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		int bf = subRL->_bf;

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

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

	int Height(Node* root)
	{
		if (root == NULL)
		{
			return 0;
		}

		return max(Height(root->_left), Height(root->_right)) + 1;
	}

	bool _IsBalance(Node* root)
	{
		if (root == NULL)
		{
			return true;
		}

		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);

		if (rightHeight - leftHeight != root->_bf)
		{
			cout << "平衡因子异常:" << root->_kv.first << endl;
		}

		return abs(leftHeight - rightHeight) < 2 && _IsBalance(root->_left) && _IsBalance(root->_right);
	}

	bool IsBalance()
	{
		return _IsBalance(_root);
	}


	//遍历的时候 root为private外面无法拿到
	//因此需要封装一层
	void _Inorder(Node* root)
	{
		if 以上是关于AVLTree(二叉平衡树)底层实现的主要内容,如果未能解决你的问题,请参考以下文章

AVLTree(二叉平衡树)底层实现

学习数据结构笔记(12) --- [平衡二叉搜索树(AVLTREE)]

13-自平衡二分搜索树 AVLTree

平衡二叉树的旋转类型及代码实现

二叉平衡树的实现(c语言编程)

平衡二叉树(AVLTREE,双链表实现)