C++之AVL树介绍及代码实现

目录

一、AVL树介绍

二、AVL树的实现

2.1 节点的定义

2.2 节点的插入

2.3 AVL树的旋转

2.3.1 左旋

2.3.2 右旋

2.3.3 右左旋

2.3.4 左右旋

2.4 总体代码实现


一、AVL树介绍

        学习过二叉搜索树后,虽然它可以缩短查找的效率,但当数据有序或接近有序时,二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下,因此AVL树就诞生了。

AVL树可以是空树,也可以是具有以下性质的搜索二叉树

  • 左右子树都是AVL树
  • 左右子树高度之差(平衡因子)的绝对值不超过1(可以是0、-1、1)

这样设计可以降低树的高度,从而减少平均搜索长度。

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在
O(logn),搜索时间复杂度O(logn)

二、AVL树的实现

2.1 节点的定义

struct AVLTreeNode
{
	T _data;
	int _bf;
	AVLTreeNode<T>* _left;
	AVLTreeNode<T>* _right;
	AVLTreeNode<T>* _parent;
	AVLTreeNode(const T& data)
		:_data(data)        //节点的值
		,_left(nullptr)    //节点的左孩子
		,_right(nullptr)    //节点的右孩子
		,_parent(nullptr)   //节点的父亲
		,_bf(0)            //节点的平衡因子
	{}
};

2.2 节点的插入

在插入时,我们要不断更新平衡因子,才能根据平衡因子来检测AVL树的平衡性。

平衡因子的计算方式是右子树-左子树,在我们插入一个节点后,显然它的parent的平衡因子会改变,根据计算方式可以知道:

  • 新增在左,parent平衡因子减1
  • 新增在右,parent平衡因子加1

根据更新后的平衡因子我们要怎么做出相应的动作而维持平衡性呢?

  1. 更新后parent的平衡因子 == 0,说明parent所在子树的高度不变,不会再影响祖先,不用再继续沿着root的路径往上更新
  2. 更新后parent的平衡因子 == 1 或 -1,说明parent所在子树的高度发生变化,会再影响祖先,因此需要再继续沿着root的路径往上更新
  3. 更新后parent的平衡因子 == 2 或 -2,说明parent所在子树的高度发生变化并且不平衡了,接下来就需要对其所在的子树进行旋转,让他平衡。

2.3 AVL树的旋转

在旋转时,我们要注意:

  1. 保证它还是搜索树
  2. 将其旋转为平衡树并且降低这个子树的高度

在旋转时一共有四种情况:

2.3.1 左旋

如上图,当 parent->_bf == 2 && cur->_bf == 1 时,我们对parent进行左单旋,可以理解为把cur往上提,把cur的左边给parent的右边,把parent给cur的左边。

来看下代码:

void RotateL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = cur->_left;
		parent->_right = curleft;    //cur的左给parent的右
		if (curleft)
		{
			curleft->_parent = parent;    //cur的左向上找自己的_parent:parent
		}
		cur->_left = parent;              //parent给cur的左
		Node* ppnode = parent->_parent;   //ppnode记录旋转前parent的_parent
		parent->_parent = cur;            //parent向上找旋转后自己的_parent:cur
		if (parent == _root)              //如果parent就是根节点,旋转后cur成为根
		{
			_root = cur;
			cur->_parent = nullptr;
		}    
		else                                //如果不是根节点,则看原来parent的位置,将cur对其
		{                                   //位置替换
			if (ppnode->_left == parent)    
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;
			}
			cur->_parent = ppnode;            //更新cur的_parent
		}
		parent->_bf = cur->_bf = 0;            //旋转后平衡因子均为0
	}

2.3.2 右旋

如上图,当 parent->_bf == -2 && cur->_bf == -1 时,我们对parent进行右单旋,可以理解为把cur往上提,把cur的右边给parent的左边,把parent给cur的右边。

代码思想与左旋是一样的:

void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;
		parent->_left = curright;
		if (curright)
		{
			curright->_parent = parent;
		}
		cur->_right = 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;
	}

2.3.3 右左旋

当遇到有拐点的情况时,我们就要进行双旋:如上图,先对cur进行右旋,再对parent进行左旋

旋转我们可以复用上面写过的代码,不过这里更新平衡因子要发生变化: 这里平衡因子受到最下面节点(我们称为curleft)平衡因子的影响:

  1. 如果curleft的平衡因子为0,那么它只能是没有孩子的节点,那么最终旋转到上面这三个节点的平衡因子都是为0的
  2. 如果curleft的平衡因子为1,如下图,可以看出旋转后parent的平衡因子变为-1,其余的都为0

        3. 如果curleft的平衡因子为 -1,如下图,可以看出旋转后cur的平衡因子变为1,其余的都为0

因此我们可以写出代码:

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

	}

2.3.4 左右旋

如上图,它与右左旋相反,但是大体的思路是一样的,旋转后根据curright的平衡因子来调整平衡因子,分析的步骤是一样的,这里就不再介绍,直接来看代码:

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

	}

2.4 总体代码实现

经过上面的分析,我们就来看看完整的代码吧:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
template<class T>
struct AVLTreeNode
{
	T _data;
	int _bf;
	AVLTreeNode<T>* _left;
	AVLTreeNode<T>* _right;
	AVLTreeNode<T>* _parent;
	AVLTreeNode(const T& data)
		:_data(data)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
	{}
};
template<class T>
class AVLTree
{
	typedef AVLTreeNode<T> Node;
public:
	bool insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_data < data)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_data > data)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(data);
		if (parent->_data < data)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;
		//插入后更新平衡因子(=右子树高度-左子树高度)
		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)
			{
				//不满足AVL,需要翻转
				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);
			}
		}
		return true;
	}
	void RotateL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = cur->_left;
		parent->_right = 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;
		if (curright)
		{
			curright->_parent = parent;
		}
		cur->_right = 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 RotateRL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = cur->_left;
		int bf = curleft->_bf;
		RotateR(cur);
		RotateL(parent);
		if (bf == 0)
		{
			cur->_bf = 0;
			curleft->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			cur->_bf = 0;
			curleft->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			cur->_bf = 1;
			curleft->_bf = 0;
			parent->_bf = 0;
		}
		else
		{ 
0			assert(false);
		}

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

	}
	//检验AVL
	bool isAVLTree()
	{
		return isAVLTree(_root);
	}
	bool isAVLTree(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		int leftHeight = height(root->_left);
		int rightHeight = height(root->_right);
		if (root->_bf != (rightHeight - leftHeight))
		{
			cout << root->_data << "平衡因子出错" << root->_bf << endl;
			return false;
		}
		return abs(rightHeight - leftHeight) < 2 
			&& isAVLTree(root->_right) 
			&& isAVLTree(root->_left);
	}
	int height(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		int leftHeight = height(root->_left);
		int rightHeight = height(root->_right);
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

private:
	Node* _root = nullptr;
};

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值