波奇学C++:AVL树

AVL解决二叉搜索树退化成链表,保证左右子树高度不差过1,尽可能接近满二叉树

AVL树的性质:高度差(平衡因子)的绝对值不超过1(-1/0/1)

平衡因子:右子树高度-左子树高度

用平衡因子控制高度

AVL树节点

cpp 复制代码
class AVLTreeNode
{
	pair<K, V>_kv; //key/value
	AVLTreeNode<K, V>* _left; // 左
	AVLTreeNode<K, V>* _right;// 右
	AVLTreeNode<K, V>* _partent;// 父母
	int _bf;// 平衡因子
	AVLTreeNode(const pair<K,V>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
	{}
};

控制逻辑:增加一个新节点cur

parent的bf值-1,在右 ,parent的bf加1

如果parent_bf=0,则不再改变,否则继续向上调整,直到parent为0,或者为cur为根节点时。

如果parent的平衡因子为2或者-2,说明树不平衡,要进行旋转调整。

当左边高时(parent=2,cur=1)

柱子a,b,c表示高度为h的AVL树

cpp 复制代码
parent->right=cur->left
cur->left=parent

对于左单旋的理解:1.根节点向左转,2.c树高度增加后a树高2,于是把c的父节点当成根节点使得c的高度方向-1,a的树的方向+1,刚好平衡

同时调整平衡因子数量和parent指针

左单旋

cpp 复制代码
void RotateL(Node* parent)
	{
		Node* cur = parent->_right; //确定cur
		Node* curleft = parent->_left;
		parent->_left = curleft;
        //当h不为0时
		if (curleft)
		{
            //调整parent指针
			curleft->_partent = parent;
		}
		cur->_left = parent;
        //调整新的根节点的parent指针
		Node* ppnode = parent->_parent;
		parent->_parent = cur;
        //根节点特殊情况
		if (parent == _root)
		{
			_root = cur;
			cur->_partent = nullptr;
		}
		else
		{
            // 确定parent是ppnode的左指针还是右指针
			if (ppnode->_left==parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode_right = cur;
			}
			cur->_partent = ppnode;
		}
		parent->_bf = cur->_bf = 0;
	}
cpp 复制代码
void RotateR()
{
	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;
		parent->_left = curright;
		cur->_right = parent;

		Node* ppnode = parent->_parent;
		if (curright)
		{
			curright->_partent = parent;
		}
		parent->_parent = cur;
		if (ppnode == root)
		{
			_root = cur;
			cur->_partent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else(ppnode->_right == parent)
			{
				ppnode->_right = cur;
			}
		}
		cur->_bf = parent->_bf = 0;
		
	}
}

双旋:我们发现左右双旋都是在都是a,c树高度发生变化,具体体现在cur和parent的平衡因子都是相差1,而如果在b树发生变化,无论是左边高还是右边高都会产生双旋问题。具体可以分为四种情况,现在以一种情况为例:

如下图是右边高的b树(现为60-b-c子树)发生改变。

从图上来讲是90进行右旋,把高的树的树移到外面,再30进行左旋,构建平衡。

对于父节点为60的树来说,左树成为30节点的右树,右树成为90节点的左树。

所以60的节点的双旋后30,60,90的_bf值不恒为0,30,90节点会受到60的影响。

当60->_bf=1, 30,60,90 =-1 ,0,0

60->_bf=-1, 30,60,90 = 1,0,0

左高右高的区别都是60做根节点,且_bf变化是一样的。

左高:先左旋后右旋,右高,先右旋后左旋。

插入代码和旋转代码

cpp 复制代码
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 (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else if (parent->_kv.first > kv.first)
   		{
			parent->_left = cur;
		}
		cur->_parent = parent;
		return true;
		// 更新节点
		while (parent)
		{
			if (cur == parent->_left)
			{
				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)
			{
				if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					//右高双旋
					RotateRL(parent);
				}
				else
				{
					//左高双旋
					RotateLR(parent);
				}

			}
			else
			{
				assert(false);
			}
		}
		return true; 
	}
	void RotateL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = parent->_left;
		parent->_left = curleft;
		if (curleft)
		{
			curleft->_parent = parent;
		}
		cur->_left = parent;
		Node* ppnode = parent->_parent;
		parent->_parent = cur;
		if (parent == _root)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left==parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;
			}
			cur->_parent = ppnode;
		}
		parent->_bf = cur->_bf = 0;
	}	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;
		parent->_left = curright;
		cur->_right = parent;

		Node* ppnode = parent->_parent;
		if (curright)
		{
			curright->_parent = parent;
		}
		parent->_parent = cur;
		if (ppnode == _root)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;
			}
		}
		cur->_bf = parent->_bf = 0;
		
	}
	//右高先右后左
	void RotateRL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = cur->_left;
		int bf = curleft->_bf;
		RotateR(parent->_right);
		RotateL(parent);
		if (bf == 1)
		{
			cur->_right->_bf = 0;
			cur->_left->_bf= -1;

		}
		else if (bf == -1)
		{
			cur->_right->_bf = 0;
			cur->_left->_bf = 1;
		}
		else
		{
			;
		}
	}
	void RotateLR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;
		int bf = curright->_bf;
		RotateL(cur);
		RotateR(parent);
		if(bf == 1)
		{
			cur->_right->_bf = 0;
			cur->_left->_bf = -1;

		}
		else if(bf==-1)
		{
		cur->_right->_bf = 0;
		cur->_left->_bf = 1;
		}
		else
		{
			;
		}
	}

检验AVL树

cpp 复制代码
int Height(Node* root)
	{
		if (root == nullptr)
			return true;
		int leftHight = Height(root->_left);
		int right = Height(root->_right);
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeigth + 1;
	}
	bool IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;
		int leftHight = Height(root->_left);
		int rightHight = Height(root->_right);
		if (rightHight-leftHight!=root->_bf)
		{
			cout<<"平衡因子异常"
		}
		return abs(rightHight - leftHight) < 2
			&& IsBalance(root->_left)
			&& IsBalance(root->_right);
	}
相关推荐
pzx_00120 分钟前
【LeetCode】94.二叉树的中序遍历
算法·leetcode·职场和发展
我曾经是个程序员24 分钟前
使用C#生成一张1G大小的空白图片
java·算法·c#
芒果de香蕉皮1 小时前
mavlink移植到单片机stm32f103c8t6,实现接收和发送数据
stm32·单片机·嵌入式硬件·算法·无人机
徐子童1 小时前
二分查找算法专题
数据结构·算法
小王子10241 小时前
数据结构与算法Python版 二叉查找树
数据结构·python·算法·二叉查找树
灰勒塔德1 小时前
Linux-----进程处理(文件IO资源使用)
linux·运维·算法
xiaoshiguang31 小时前
LeetCode:404.左叶子之和
java·算法·leetcode
计科土狗2 小时前
前缀和与差分
c++·算法
时清云2 小时前
【算法】 课程表
前端·算法·面试
凭君语未可2 小时前
详解归并排序
算法·排序算法