C++进阶——AVL树的构建

C++进阶——AVL树的构建

AVL树

概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当
于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年
发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之
差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
它的左右子树都是AVL树
左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
在这里插入图片描述

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

AVL树节点的定义

template<class K,class V>
	struct AVLTreeNode
	{
		AVLTreeNode(const pair<k,v>& kv)//节点构造函数
			:_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_kv(kv)
			,_be(0)
		{

		}

		AVLTreeNode<K, V>* _left;
		AVLTreeNode<K, V>* _right;
		AVLTreeNode<K, V>* _parent;
		pair<k, v> _kv;
		int _be;//balance element
	};

AVL树节点的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入
过程可以分为两步:

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子

那如果满足不了平衡因子该怎么办呢?
没错,就是旋转、跳跃、我不停歇
右右——单左旋
在这里插入图片描述

void RotateL(node* parent)
		{
			node* subR = parent->_right;
			node* subRL = subR->_left;

			parent->_right = subRL;
			if(subRL)
				subRL->parent = parent;

			node* ppnode = parent->_parent;
			subR->_left = parent;
			parent->_parent = subR;

			if (ppnode == nullptr)
			{
				_root = subR;
				_root->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
				{
					ppnode->_left = subR;
				}
				else
				{
					ppnode->_right = subR;
				}
				subR->_parent = ppnode;
			}

			parent->_be = subR->_be = 0;
		}

左左——单右旋

在这里插入图片描述

void RotateR(node* parent)
		{
			node* subL = parent->_left;
			node* subLR = subL->_right;

			subL->_left = subLR;
			
			if (subLR)
				subLR->_parent=parent;

			node* ppnode = parent->_parent;
			subL->_right = parent;
			parent->_parent = subL;
			if (parent == _root)
			{
				_root = subL;
				_root->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
				{
					ppnode->_left = subL;
				}
				else
				{
					ppnode->_right=subL;
				}
				subL->_parent = ppnode;
			}
			subL->_be = parent->_be = 0;
		}

新节点插入较高左子树的右侧—左右:先左单旋再右单旋
在这里插入图片描述

void RotateLR(node* parent)
		{
			node* subL = parent->_left;
			node* subLR = subL->_right;
			int be = subLR->_be;

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

			if (be == 1)
			{
				parent->_be = 0;
				subLR->_be = 0;
				subL->_bf = -1;
			}
			else if (be == -1)
			{
				parent->_be = 1;
				subLR->_be = 0;
				subL->_be = 0;
			}
			else if (be == 0)
			{
				parent->_be = 0;
				subLR->_be = 0;
				subL->_be = 0;
			}
			else
			{
				assert(false);
			}
		}

新节点插入较高右子树的左侧—右左:先右单旋再左单旋
在这里插入图片描述

void RotateRL(node* parent)
		{
			node* subR = parent->_right;
			node* subRL = subR->_left;
			int be = subRL->_be;
			RotateR(parent->_right);
			RotateL(parent);
			if (be == 1)
			{
				parent->_be = 0;
				subRL->_be = 0;
				subR->_bf = -1;
			}
			else if (be == -1)
			{
				parent->_be = 1;
				subRL->_be = 0;
				subR->_be = 0;
			}
			else if (be == 0)
			{
				parent->_be = 0;
				subRL->_be = 0;
				subR->_be = 0;
			}
			else
			{
				assert(false);
			}
		}

总结:
假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑

  1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR
    当pSubR的平衡因子为1时,执行左单旋
    当pSubR的平衡因子为-1时,执行右左双旋
  2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL
    当pSubL的平衡因子为-1是,执行右单旋
    当pSubL的平衡因子为1时,执行左右双旋
    旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。

判断是否是平衡树

一下函数可以判断是否是平衡树,还有计算树的高度。

int _Height(node* pRoot)
		{
			if (pRoot == nullptr)
				return 0;
			int leftHight = _Height(pRoot->_left);
			int rightHight = _Height(pRoot->_right);

			return leftHight > rightHight ? leftHight + 1:rightHight + 1;
		}
		bool _IsBalanceTree(node* pRoot)
		{
			// 空树也是AVL树
			if (nullptr == pRoot) return true;
			// 计算pRoot节点的平衡因子:即pRoot左右子树的高度差
			int leftHeight = _Height(pRoot->_pLeft);
			int rightHeight = _Height(pRoot->_pRight);
			int diff = rightHeight - leftHeight;
			// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者
			// pRoot平衡因子的绝对值超过1,则一定不是AVL树
			if (diff != pRoot->_bf || (diff > 1 || diff < -1))
				return false;
			// pRoot的左和右如果都是AVL树,则该树一定是AVL树
			return _IsBalanceTree(pRoot->_pLeft) && _IsBalanceTree(pRoot->_pRight);
		}

AVL树的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证
查询时高效的时间复杂度,即。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:
插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。
因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,
但一个结构经常修改,就不太适合。

整体的代码:

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

namespace tom 
{
	template<class K,class V>
	struct AVLTreeNode
	{
		AVLTreeNode(const pair<K,V>& kv)//节点构造函数
			:_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_kv(kv)
			,_be(0)
		{

		}

		AVLTreeNode<K, V>* _left;
		AVLTreeNode<K, V>* _right;
		AVLTreeNode<K, V>* _parent;
		pair<K, V> _kv;
		int _be;//balance element
	};

	template<class K, class V>
	class AVLTree
	{
		typedef AVLTreeNode<K, V> node;
	public:
		bool insert(const pair<K, V>& kv)
		{
			if (_root == nullptr)
			{
				_root = new node(kv);
				return true;
			}

			node* cur = _root;
			node* parent = nullptr;
			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->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			cur->_parent = parent;

			while (parent)
			{
				if (cur == parent->_right)
				{
					parent->_be++;
				}
				else
				{
					parent->_be--;
				}

				if (parent->_be == 1 || parent->_be == -1)
				{
					parent = parent->_parent;
					cur = cur->_parent;
				}
				else if (parent->_be == 0)
				{
					break;
				}
				else if (parent->_be == 2 || parent->_be == -2)
				{
					if (parent->_be == -2 && cur-> - 1)
					{
						RotateR(parent);
					}
					else if (parent->_be == 2 && cur-> 1)
					{
						RotateL(parent);
					}
					else if (parent->_be == 2 && cur-> - 1)
					{
						RotateRL(parent);
					}
					else if (parent->_be == -2 && cur-> 1)
					{
						RotateLR(parent);
					}
					else
					{
						assert(false);
					}
				}
			}
		}
	private:
		void RotateL(node* parent)
		{
			node* subR = parent->_right;
			node* subRL = subR->_left;

			parent->_right = subRL;
			if(subRL)
				subRL->parent = parent;

			node* ppnode = parent->_parent;
			subR->_left = parent;
			parent->_parent = subR;

			if (ppnode == nullptr)
			{
				_root = subR;
				_root->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
				{
					ppnode->_left = subR;
				}
				else
				{
					ppnode->_right = subR;
				}
				subR->_parent = ppnode;
			}

			parent->_be = subR->_be = 0;
		}
		void RotateR(node* parent)
		{
			node* subL = parent->_left;
			node* subLR = subL->_right;

			subL->_left = subLR;
			
			if (subLR)
				subLR->_parent=parent;

			node* ppnode = parent->_parent;
			subL->_right = parent;
			parent->_parent = subL;
			if (parent == _root)
			{
				_root = subL;
				_root->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
				{
					ppnode->_left = subL;
				}
				else
				{
					ppnode->_right=subL;
				}
				subL->_parent = ppnode;
			}
			subL->_be = parent->_be = 0;
		}
		void RotateRL(node* parent)
		{
			node* subR = parent->_right;
			node* subRL = subR->_left;
			int be = subRL->_be;
			RotateR(parent->_right);
			RotateL(parent);
			if (be == 1)
			{
				parent->_be = 0;
				subRL->_be = 0;
				subR->_bf = -1;
			}
			else if (be == -1)
			{
				parent->_be = 1;
				subRL->_be = 0;
				subR->_be = 0;
			}
			else if (be == 0)
			{
				parent->_be = 0;
				subRL->_be = 0;
				subR->_be = 0;
			}
			else
			{
				assert(false);
			}
		}
		void RotateLR(node* parent)
		{
			node* subL = parent->_left;
			node* subLR = subL->_right;
			int be = subLR->_be;

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

			if (be == 1)
			{
				parent->_be = 0;
				subLR->_be = 0;
				subL->_bf = -1;
			}
			else if (be == -1)
			{
				parent->_be = 1;
				subLR->_be = 0;
				subL->_be = 0;
			}
			else if (be == 0)
			{
				parent->_be = 0;
				subLR->_be = 0;
				subL->_be = 0;
			}
			else
			{
				assert(false);
			}
		}
	private:
		node* _root = nullptr;

	};
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Tom王要coding

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

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

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

打赏作者

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

抵扣说明:

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

余额充值