【c++】手撕AVL树

一、什么是AVL树

二叉树搜索树存在缺点,就是二叉搜索树如果不平衡左子树和右子树高度相差较多时,搜索的效率会降低。甚至可能会退化成链表。就如同下图。

这时候我们可以加入一个平衡因子,来解决这个问题

平衡因子 = 右子树的高度 - 左子树的高度,且平衡因子的绝对值不超过1.这个就是AVL树,也叫二叉搜索平衡树。


二、AVL树的框架

这是AVL树的基本框架

#include<iostream>
using namespace std;

template<class T>
struct AVLTreeNode
{
    T _key;
    AVLTreeNode<T>* _left;
    AVLTreeNode<T>* _right;
    AVLTreeNode<T>* _parent;
    AVLTreeNode(const T& key)
        :_key(key)
        ,_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        {}
};


template<class T>
class AVLTree
{
    typedef AVLTreeNode<T> Node;
public:
    
private:
    Node* _root = nullptr;
    int _bf = 0;
};

这里使用struct来定义AVL树的节点是因为struct默认是public,方便后面的使用。


三、AVL树的插入

因为我们的AVL树本质上也是一颗二叉搜索树,所以我们插入的过程,首先也是和二叉搜索树的插入过程一样,先循环比较找到要插入的位置。

bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key == key)
			{
				return false;
			}
			else
			{
				break;
			}
		}
		cur = new Node(key);
		if (cur->_key > parent->_key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

        .......

然后这时候我们就要考虑平衡因子的问题,我们会遇到以下几种情况。

插入的节点的平衡因子肯定是0,然后我们再根据插入的节点在父节点的左边还是右边,如果在左边,bf--,如果在右边,bf++

1.如果插入后父节点的平衡因子为0,则说明这个节点平衡,不需要调整。

插入在根节点的右边

父节点的bf++,变成0,则不需要再做处理。

2.如果父节点的平衡因子是1或者-1,说明肯定是有一边变高了,就会影响到祖先节点,这时候我们就需要继续向上处理。

就比如

我们插入前

插入一个节点在最右节点的右边

cur在paren的右边,则parent的bf++,变成1.

然后再看parent的parent,也就是根节点,对于根节点来说,右子树增高了,

所以bf++,变成1.

3,插入后parent的bf为2或者-2,

这时候我们就需要通过旋转来调整,来让bf为0。

就比如我们插入后,调整bf到p的位置

我们的右子树高,所以要通过左旋来调整。

左旋的核心步骤是

p -> _right = cur->_left

c->_left = p;

然后在调节一下我们的父子节点关系和bf即可。

右旋也是一样。

4.右左旋

右左旋是当我们的parentbf为2,cur的bf为-1,是个折线的时候,就比如

先将cur右旋,再将parent左旋。

然后再调节平衡因子。我们要根据插入的节点的父节点情况来调节平衡因子。也就是根据图上的根节点它插入后没旋转之前的情况来调节。

左右双旋和右左双旋思路也是一样的。


这是整个插入过程的代码

bool Insert(const pair<K,V>& val)
	{
		if (_root == nullptr)
		{
			_root = new Node(val);
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (cur->_val.first > val.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_val.first < val.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_val.first == val.first)
			{
				return false;
			}
			else
			{
				break;
			}
		}
		cur = new Node(val);
		if (cur->_val.first > parent->_val.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		while (parent)
		{
			//插入的节点在父节点的左边,bf--
			if (parent->_left == cur)
			{
				parent->_bf--;
			}
			//插入的节点在父节点的右边,bf++
			else
			{
				parent->_bf++;
			}

			//如果父节点的bf的绝对值为1,则不需要旋转,向上调节节点。
			if (parent->_bf == -1 || parent->_bf == 1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			//如果父节点的bf为0,则不需要向上调整
			else if (parent->_bf == 0)
			{
				break;
			}
			//如果父节点的bf为2或-2,则需要旋转
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotationL(parent);
					break;
				}
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotationR(parent);
					break;
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotationRL(parent);
					break;
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotationLR(parent);
					break;
				}
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}
	void RotationL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = cur->_left;
		parent->_right = curleft;

		if (curleft)
			curleft->_parent = parent;
		Node* ppnode = parent->_parent;
		cur->_left = 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 RotationR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;
		Node* ppnode = parent->_parent;

		//开始右旋,将cur的右接parent,cur的右节点接到parent的左
		cur->_right = parent;
		parent->_left = curright;
		
		//如果parent是root,就让cur变成root
		if (parent == _root)
		{
			cur = _root;
			cur->_parent = nullptr;
		}
		//处理parent
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;
			}
			cur->_parent = ppnode;
		}
		parent->_parent = cur;
		if(curright)
			curright->_parent = parent;

		cur->_bf = parent->_bf = 0;
	}

	void RotationRL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = cur->_left;
		
		//先把bf存起来,因为调用左旋和右旋函数会把bf给修改成0
		//curbf是一会判断ppnode和parent的bf为什么的因素
		int curleftbf = curleft->_bf;
		
		RotationR(cur);
		RotationL(parent);
		if (curleftbf == 0)
		{
			cur->_bf = curleft->_bf = parent->_bf = 0;
		}
		else if (curleftbf == 1)
		{
			curleft->_bf = cur->_bf = 0;
			parent->_bf = -1;
		}
		else if (curleftbf == -1)
		{
			curleft->_bf = parent->_bf = 0;
			cur->_bf = 1;
		}
		else
		{
			assert(false);
		}
	}

	void RotationLR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;
		int currightbf = curright->_bf;

		//左右旋
		RotationL(cur);
		RotationR(parent);

		if (currightbf == 0)
		{
			cur->_bf = parent->_bf = curright->_bf = 0;
		}
		else if (currightbf == 1)
		{
			curright->_bf = parent->_bf = 0;
			cur->_bf = -1;
		}
		else if (currightbf == -1)
		{
			curright->_bf = cur->_bf = 0;
			parent->_bf = 1;
		}
		else
		{
			assert(false);
		}
	}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ck837

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

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

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

打赏作者

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

抵扣说明:

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

余额充值