C++:图解AVL平衡二叉树的原理

C++:图解AVL平衡二叉树的原理

1. AVL树的性质

AVL树是一种平衡二叉树,一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

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

在这里插入图片描述

  • 上图是一个带平衡因子的平衡二叉树(右子树高度差为正,左子树高度差为负)
  • 定义一个AVL树的节点类模板
template<class T>//构建AVL树节点类模板
struct AVLTreeNode
{
	AVLTreeNode(const T& val = T())
		:left(nullptr)
		, right(nullptr)
		, parent(nullptr)//回溯用的双亲
		, data(val)
		, bf(0)//平衡因子
	{}
	AVLTreeNode<T>* left;
	AVLTreeNode<T>* right;
	AVLTreeNode<T>* parent;
	T data;
	int bf;
};

2. AVL树的操作

  • 查找操作:因为AVL树总是保持平衡的,所以查找操作和二叉搜索树完全一致
  • 插入操作:AVL的插入操作和二叉搜索树的插入操作是相似的,只不过在插入后需要对树的高度进行调整,因此就需要用到旋转操作中调整的操作。
  • 删除操作:AVL树的删除操作和二叉查找树也相似,只不过删除元素后要检查树是否失去平衡,如果失衡就需要重新调整平衡,并更新平衡因子的值。
  • 旋转操作:AVL树中由于平衡因子的作用,当在一个AVL树中插入新节点时,很可能会导致AVL树的平衡结构被破坏,具体表现为平衡因子不为(0/1/-1),此时就必须要对AVL树的结构进行调整,调整的方法就是旋转树的节点,具体方法如下:
  1. RR旋转(右子树的右子树上插入结点)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. LL旋转(左子树的左子树上插入结点)以下的回溯过程图示省略
    在这里插入图片描述
    在这里插入图片描述

  2. LR旋转(左子树的右子树上插入结点)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. RL旋转(右子树的左子树上插入结点)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3. AVL树的实现

  • 只实现了旋转和插入方法,不包含析构,代码仅用于测试
#include<iostream>
#include<stack>
using namespace std;

template<class Type>
class AVLTree;

//avl node
template<class Type>
class AVLNode
{
	friend class AVLTree<Type>;
public:
	AVLNode(Type d = Type(), AVLNode<Type>* left = nullptr, AVLNode<Type>* right = nullptr)
		: data(d)
		, leftChild(left)
		, rightChild(right)
		, bf(0)
	{}
	~AVLNode()
	{
	}
private:
	Type data;
	AVLNode<Type>* leftChild;
	AVLNode<Type>* rightChild;
	int            bf; //平衡因子
};

//avl tree
template<class Type>
class AVLTree
{
public:
	AVLTree() :root(nullptr)
	{}
public:
	bool Insert(const Type& x)
	{
		return Insert(root, x);
	}
	bool Remove(const Type& key)
	{
		return Remove(root, key);
	}
	void InOrder()
	{
		_InOrder(root);
		cout << endl;
	}
protected:
	bool Insert(AVLNode<Type>*& t, const Type& x);
	bool Remove(AVLNode<Type>*& t, const Type& key);
	void RotateR(AVLNode<Type>*& ptr);
	void RotateLR(AVLNode<Type>*& ptr);
	void RotateL(AVLNode<Type>*& ptr);
	void RotateRL(AVLNode<Type>*& ptr);
private:
	void _InOrder(AVLNode<Type>* pRoot)//中序遍历
	{
		if (pRoot)
		{
			_InOrder(pRoot->leftChild);
			cout << pRoot->data << " ";
			_InOrder(pRoot->rightChild);
		}
	}
	AVLNode<Type>* root;
};

//1 调整平衡  2更改bf
template<class Type>
void AVLTree<Type>::RotateR(AVLNode<Type>*& ptr)
{
	AVLNode<Type>* subR = ptr;
	ptr = subR->leftChild;

	subR->leftChild = ptr->rightChild;
	ptr->rightChild = subR;

	ptr->bf = subR->bf = 0;
}

template<class Type>
void AVLTree<Type>::RotateLR(AVLNode<Type>*& ptr)
{
	AVLNode<Type>* subL = ptr->leftChild;
	AVLNode<Type>* subR = ptr;
	ptr = subL->rightChild;

	subL->rightChild = ptr->leftChild;
	ptr->leftChild = subL;
	//更新 subL->bf
	if (ptr->bf <= 0)
		subL->bf = 0;
	else
		subL->bf = -1;

	subR->leftChild = ptr->rightChild;
	ptr->rightChild = subR;
	//更新 subR->bf
	if (ptr->bf >= 0)
		subR->bf = 0;
	else
		subR->bf = 1;

	ptr->bf = 0;
}

template<class Type>
void AVLTree<Type>::RotateL(AVLNode<Type>*& ptr)
{
	AVLNode<Type>* subL = ptr;
	ptr = subL->rightChild;
	subL->rightChild = ptr->leftChild;
	ptr->leftChild = subL;

	ptr->bf = subL->bf = 0;
}

template<class Type>
void AVLTree<Type>::RotateRL(AVLNode<Type>*& ptr)
{
	AVLNode<Type>* subL = ptr;
	AVLNode<Type>* subR = ptr->rightChild;
	ptr = subR->leftChild;

	subR->leftChild = ptr->rightChild;
	ptr->rightChild = subR;
	//更新subR->bf
	if (ptr->bf >= 0)
		subR->bf = 0;
	else
		subR->bf = 1;

	subL->rightChild = ptr->leftChild;
	ptr->leftChild = subL;
	//更新subL->bf
	if (ptr->bf == 1)
		subL->bf = -1;
	else
		subL->bf = 0;

	ptr->bf = 0;
}

template<class Type>
bool AVLTree<Type>::Insert(AVLNode<Type>*& t, const Type& x)
{
	//1 按照BST的规则插入节点
	AVLNode<Type>* pr = nullptr;
	AVLNode<Type>* p = t;

	stack<AVLNode<Type>*> st;

	while (p != nullptr)
	{
		//用于查找x的插入位置
		if (x == p->data)
			return false;

		pr = p;
		st.push(pr);

		if (x < p->data)
			p = p->leftChild;
		else
			p = p->rightChild;
	}
	p = new AVLNode<Type>(x);
	if (pr == nullptr)
	{
		t = p;
		return true;
	}

	//链接新建节点
	if (x < pr->data)
		pr->leftChild = p;
	else
		pr->rightChild = p;

	//2 如果发生不平衡,需要调整平衡
	while (!st.empty())
	{
		pr = st.top();
		st.pop();
		if (p == pr->leftChild)
			pr->bf--;
		else
			pr->bf++;

		if (pr->bf == 0)
			break;
		if (pr->bf == 1 || pr->bf == -1) // |bf| == 1 向上回溯
			p = pr;
		else
		{
			//|bf| == 2 发生不平衡 调整平衡
			if (p == pr->leftChild) //左分支
			{
				if (p->bf < 0)    //     /
				{
					RotateR(pr);
					//cout<<"RotateR."<<endl;
				}
				else            //     <
				{
					RotateLR(pr);
					//cout<<"RotateLR."<<endl;
				}
			}
			else  //右分支
			{
				if (p->bf > 0)    //   \ 
				{
					RotateL(pr);
					//cout<<"RotateL."<<endl;
				}
				else             //   > 
				{
					RotateRL(pr);
					//cout<<"RotateRL."<<endl;
				}
			}
			break;
		}
	}

	//重新链接
	if (st.empty())
		t = pr;
	else
	{
		AVLNode<Type>* ppr = st.top();
		if (ppr->data > pr->data)
			ppr->leftChild = pr;
		else
			ppr->rightChild = pr;
	}

	return true;
}

template<class Type>
bool AVLTree<Type>::Remove(AVLNode<Type>*& t, const Type& key)
{
	//1 按照bst的规则找到节点并删除

	//2 删除节点后,若发生不平衡,需要重新调整并更新bf
}

void main()
{
	int ar[] = { 9,26,16,3,7,11,18,14,15 };
	int n = sizeof(ar) / sizeof(ar[0]);

	AVLTree<int> avl;
	for (int i = 0; i < n; ++i)
		avl.Insert(ar[i]);

	avl.InOrder();

	avl.Insert(6);
	avl.InOrder();

	avl.Insert(12);
	avl.InOrder();

	avl.Insert(0);
	avl.InOrder();

	avl.Insert(4);
	avl.InOrder();
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值