C/C++ 进阶(5)二叉平衡搜索树(AVL树)

个人主页:仍有未知等待探索-CSDN博客

专题分栏:C++

目录

一、概念

节点结构 

二、平衡因子

三、操作

插入

旋转

左单旋

右单旋

左右双旋

右左双旋

四、总代码


一、概念

二叉平衡搜索树又称AVL树,是一种特殊的二叉搜索树。一般的二叉搜索树在遇到数据有序时,查找的效率比较的低。

而二叉平衡搜索树,为了防止有这种极端的情况出现,其保证了任一节点的左右子树的高度差不超过1,通过控制高度,使查找的次数降低。 

节点结构 

左孩子,右孩子,双亲指针,值,平衡因子

template<class T, class V>
struct AVLN
{
	typedef pair<T, V> PTV;
	AVLN(const PTV& e)
		:parent(nullptr)
		,left(nullptr)
		,right(nullptr)
		,val(e)
		,bf(0)
	{}
	AVLN<T, V>* parent;
	AVLN<T, V>* left;
	AVLN<T, V>* right;
	PTV val;
	int bf;
};

二、平衡因子

二叉平衡搜索树,在对节点的结构进行定义的时候多添加了一个平衡因子的变量(bf),用来判断树是否是平衡的

平衡因子 = 右子树的高度 - 左子树的高度

平衡因子的取值为 :0、+1、-1、+2、-2

当平衡因子为+2、-2的时候,代表了这棵树需要进行调整。

三、操作

插入

插入的步骤就是这些。

为什么调节完,平衡因子为1、-1的时候要向上进行调整? 

如果调节完,平衡因子为1、-1,则在没有调整的时候,平衡因子为0。新增了一个结点之后,高度发生了变化。以当前节点为根节点的子树的高度发生了变化,则以当前节点为其中的一个节点的子树的高度也同样发生了变化,所以需要进行调整。

旋转

对于要通过旋转进行调节平衡因子的情况不细分的话,有4种:左单旋、右单旋、左右双旋、右左双旋。

注意:在进行旋转的时候,对于每个节点的指针变化,要特别的细心。

节点中有三个指针,一个指向左孩子、一个指向右孩子、另外一个指向双亲。

旋转点:进行旋转的一个基准点。就比如左单旋的话,parent就是旋转点。

左单旋

左单旋变换:subrl 变成 parent 的右子树,parent 成为 subr 的左子树。

直接看图变换是不难,但是写代码就会有很多的坑(指针的变换)。

// 左单旋
// 参数是图中的parent,也称为旋转点
void RotateL(node* parent)
{
	node* subr = parent->right;
	node* subrl = subr->left;
    
    // subrl 成为 parent 的右子树 
	parent->right = subrl;
	if (subrl)
		subrl->parent = parent;

	subr->left = parent;
	node* pparent = parent->parent;

	parent->parent = subr;

	if (_root != parent)
	{

		if (pparent->left == parent)
		{
			pparent->left = subr;
		}
		else 
		{
			pparent->right = subr;
		}
		subr->parent = pparent;
	}
	else
	{
		_root = subr;
		subr->parent = nullptr;
	}

	parent->bf = subr->bf = 0;
}

右单旋

如图是右单旋的情况。

右单旋的变换:sublr 变成 parent 的左子树,将parent 变成 subl 的右子树。

直接看图变换是不难,但是写代码就会有很多的坑(指针的变换)。

// 右单旋
// parent 为旋转点
void RotateR(node* parent)
{
	node* subl = parent->left;
	node* sublr = subl->right;
    
    // sublr成为parent的左子树
	parent->left = sublr;
    // 如果sublr是空的话,不需要更新它的双亲结点,因为空指针不能进行解引用
	if (sublr)
		sublr->parent = parent;
    
    // parent成为subl的右子树
	subl->right = parent;
    // 记录一下这个子树的根节点的双亲结点
	node* pparent = parent->parent;
    
    // 更新parent的双亲节点
	parent->parent = subl;
    
    // 判断该子树的根节点是不是整个子树的根节点
    // 不是的话,也需要更新该子树的根节点的双亲结点
	if (_root != parent)
	{
		
		if (pparent->left == parent)
		{
			pparent->left = subl;
		}
		else 
		{
			pparent->right = subl;
		}
		subl->parent = pparent;
	}
	else
	{
		_root = subl;
		subl->parent = nullptr;
	}
    
    // 更新平衡因子
	parent->bf = subl->bf = 0;
}

左右双旋

左右双旋的变换:先以 subl 为旋转点进行左单旋,然后再以 parent 为旋转点进行右单旋。

注意:1、指针的变换 2、注意新的节点插入在 b 子树的左子树和右子树的时候,平衡因子的变化。

 

// 左右双旋
// 传参传的节点还是parent
void RotateLR(node* parent)
{
	node* psubl = parent->left;
	node* psublr = psubl->right;
    
    // 记录之前的 sublr 节点的平衡因子
	int bf = psublr->bf;
    
    // 先以 subl 为旋转点进行左单旋
	RotateL(parent->left);
    // 然后再以 parent 为旋转点进行右单旋
	RotateR(parent);
    
    // 在完成旋转调整平衡后,在对该子树的平衡因子进行变换
    // 至于为什么平衡因子等于这些值,可以去看看我画的图然后进行对比
    // 还是比较清楚的
	if (-1 == bf)
	{
		psubl->bf = psublr->bf = 0;
		parent->bf = 1;
	}
	else if (1 == bf)
	{
		parent->bf = psublr->bf = 0;
		psubl->bf = -1;	
	}
	else if (0 == bf) // 如果在旋转之前的平衡因子就是为0,则调整完之后的平衡因子都是0
	{
		parent->bf = psubl->bf = psublr->bf = 0;
	}
	else // 这种情况一般是没有的,如果有,则是你写的AVL树是有错误的
	{
		assert(false);
	}
}

右左双旋

右左双旋的变换:先以 subr 为旋转点进行右单旋,然后再以 parent 为旋转点进行左单旋。

注意:1、指针的变换 2、注意新的节点插入在 c 子树的左子树和右子树的时候,平衡因子的变化。

// 右左双旋
void RotateRL(node* parent)
{
	node* psubr = parent->right;
	node* psubrl = parent->left;
	int bf = psubrl->bf;
    
    // 先以subr为旋转点进行右单旋
	RotateR(parent->right);
    // 再以parent为旋转点进行左单旋
	RotateL(parent);
    
    // 更新平衡因子
	if (1 == bf)
	{
		psubr->bf = psubrl->bf = 0;
		parent->bf = -1;
	}
	else if (-1 == bf)
	{
		parent->bf = psubrl->bf = 0;
		psubr->bf = 1;
	}
	else if (0 == bf)
	{
		parent->bf = psubr->bf = psubrl->bf = 0;
	}
	else 
	{
		assert(false);
	}
}

四、总代码

#pragma once


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

template<class T, class V>
struct AVLN
{
	typedef pair<T, V> PTV;
	AVLN(const PTV& e)
		:parent(nullptr)
		,left(nullptr)
		,right(nullptr)
		,val(e)
		,bf(0)
	{}
	AVLN<T, V>* parent;
	AVLN<T, V>* left;
	AVLN<T, V>* right;
	PTV val;
	int bf;
};

template<class T, class V>
class AVL
{
	typedef pair<T, V> PTV;
	typedef AVLN<T, V> node;
public:
	AVL()
		:_root(nullptr)
	{}
	void insert(const PTV& val)
	{
		if (_root == nullptr)
		{
			node* tmp = new node(val);
			_root = tmp;
			return;
		}
		node* parent = _root;
		node* cur = _root;
		while (cur)
		{
			if (cur->val.first > val.first)
			{
				parent = cur;
				cur = cur->left;
			}
			else
			{
				parent = cur;
				cur = cur->right;
			}
		}
		cur = new node(val);
		if (parent->val.first > val.first)
		{
			parent->left = cur;
		}
		else
		{
			parent->right = cur;
		}
		cur->parent = parent;

		// 更新
		update(cur);
	}
	void RotateR(node* parent)
	{
		node* subl = parent->left;
		node* sublr = subl->right;

		parent->left = sublr;
		if (sublr)
			sublr->parent = parent;

		subl->right = parent;
		node* pparent = parent->parent;

		parent->parent = subl;

		if (_root != parent)
		{
			
			if (pparent->left == parent)
			{
				pparent->left = subl;
			}
			else 
			{
				pparent->right = subl;
			}
			subl->parent = pparent;
		}
		else
		{
			_root = subl;
			subl->parent = nullptr;
		}

		parent->bf = subl->bf = 0;
	}
	void RotateL(node* parent)
	{
		node* subr = parent->right;
		node* subrl = subr->left;

		parent->right = subrl;
		if (subrl)
			subrl->parent = parent;

		subr->left = parent;
		node* pparent = parent->parent;

		parent->parent = subr;

		if (_root != parent)
		{

			if (pparent->left == parent)
			{
				pparent->left = subr;
			}
			else 
			{
				pparent->right = subr;
			}
			subr->parent = pparent;
		}
		else
		{
			_root = subr;
			subr->parent = nullptr;
		}

		parent->bf = subr->bf = 0;
	}
	// 右左双旋
	void RotateRL(node* parent)
	{
		node* psubr = parent->right;
		node* psubrl = parent->left;
		int bf = psubrl->bf;

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

		if (1 == bf)
		{
			psubr->bf = psubrl->bf = 0;
			parent->bf = -1;
		}
		else if (-1 == bf)
		{
			parent->bf = psubrl->bf = 0;
			psubr->bf = 1;
		}
		else if (0 == bf)
		{
			parent->bf = psubr->bf = psubrl->bf = 0;
		}
		else 
		{
			assert(false);
		}
	}
	// 左右双旋
	void RotateLR(node* parent)
	{
		node* psubl = parent->left;
		node* psublr = psubl->right;

		int bf = psublr->bf;

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

		if (-1 == bf)
		{
			psubl->bf = psublr->bf = 0;
			parent->bf = 1;
		}
		else if (1 == bf)
		{
			parent->bf = psublr->bf = 0;
			psubl->bf = -1;	
		}
		else if (0 == bf)
		{
			parent->bf = psubl->bf = psublr->bf = 0;
		}
		else 
		{
			assert(false);
		}
	}
	void inorder()
	{
		_inorder(_root);
	}
	bool find(const PTV& val)
	{
		node* cur = _root;
		while (cur)
		{
			if (cur->val.first > val.first)
			{
				cur = cur->left;
			}
			else if (cur->val.first < val.first)
			{
				cur = cur->right;
			}
			else
			{
				return true;
			}
		}
		return false;
	}
private:
	void _inorder(node* root)
	{
		if (root == nullptr) return;

		_inorder(root->left);
		cout << root->val.first << " " << root->val.second << endl;
		_inorder(root->right);
	}
void update(node* cur)
	{
		node* parent = cur->parent;

		while (parent)
		{
			if (parent->left == cur)
			{
				parent->bf -- ;
			}
			else
			{
				parent->bf ++ ;
			}
			
			if (parent->bf == 0) break; // 为0,不需要更新
			else if (parent->bf == 1 || parent->bf == -1)
			{
				cur = cur->parent;
				parent = cur->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 if (parent->bf == -2 && cur->bf == 1)
				{
					// 先左单旋,后右单旋
					RotateLR(parent);
				}
				// 直接退出
				break;
			}
			else
			{
				assert(false);
			}
		}
	}
private:
	node* _root;

};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仍有未知等待探索

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值