【c++】AVL树 --- 基本操作

9 篇文章 0 订阅

AVL树

一 . 概念

    二叉搜索树可以缩短查找的效率,但是如果数据有序或者接近有序的时候可能会退化为单支树,再查找的话相当于再一个顺序表中的查找, 这个时候效率就会很低, 达不到预期的效果, 比如:

    基于上面情况, 所以有了AVL树,即向二叉搜索树中插入结点的时候进行调整,使得每个结点的左右子树的高度差不超过一,这样就可以降低树的高度, 从而提高查找的效率, 比如 : 

 

二, 性质

1. 空树

2. 非空树

    a : 左右子树也都是AVL树

    b : 左右子树高度之差(平衡因子)的绝对值不超过1

 

三, 操作

    AVL树最主要的操作就是插入了, 查找就是很简单的, 和我们昨天写的二叉搜索树的查找一样的, 要说插入, 就要先说旋转, 因为插入新节点后, 可能会破坏树的平衡, 这个时候就需要对树进行旋转, 然后进行平衡因子的更新。

  • 旋转

旋转粗略的分的话就是四种情况, 但是细分的话其实就是6种情况

1. 插入较高左子树的左侧--左左--右单旋,比如 : 

      可以看见旋转之前树已经不平衡了, 旋转后50 和 100 的平衡因子都变为0, 重新平衡。

2. 插入较高右子树的右侧--友友--左单选,可以类比上面, 思路一摸一样

3.插入较高左子树的右侧--左右--先左旋再右旋

这个我们用抽象图来表示, 可以看到, 较高左子树的右侧有b,c两个结点, 这个时候我们就要分情况讨论。

i . 插入到b下 : 

  我们可以观察到, 旋转后90的平衡因子变成了1, 其余两个都是0.

ii . 插入到c下 :

我们可以看到, 旋转后30的平衡因子变为-1, 其它两个都是0。

 

综上我们可以得出结论 ; 

    如果60原来的平衡因子为-1, 旋转后它的祖父结点90的平衡因子变为1;

    如果60原来的平衡因子为1, 旋转后它的父亲结点30的平衡因子变为-1;

    我们可以形象的总结为 :    -1爷爷1, 1爸爸-1.

 

4. 插入较高右子树的左侧--右左--先右旋再左旋

同上面一样有两种情况

插入到c下 : 

    我们可以观察到, 旋转后30的平衡因子变成了-1, 其余两个都是0.

插入到b下 : 

我们可以观察到, 旋转后90的平衡因子变成了1, 其余两个都是0.

 

综上我们可以得出结论 ; 

    如果60原来的平衡因子为1, 旋转后它的祖父结点90的平衡因子变为-1;

    如果60原来的平衡因子为-1, 旋转后它的父亲结点30的平衡因子变为1;

    我们可以形象的总结为 :    1爷爷-1, -1爸爸1.

 

我们将上面两种双旋对比来看, 可以得到以下结论 : 

    左边的右边高 : -1爷爷1,1爸爸-1。

    右边的左边高 : 1爷爷-1,-1爸爸1.

 

  • 插入

1. 判空

2.搜索找到合适的位置

3. 创建新的结点, 并且和父亲结点链接

4. 调整平衡因子(旋转)

 

  • 判断是否平衡

1. 分别计算左子树和右子树的高度, 并且计算出高度差, 如果高度差的绝对值小于等于一,则平衡

2. 分别对左子树和右子树进行递归调用

 

 四, 代码

AVL树基本代码如下, 现在还没有写删除操作, 后续会补上。

#include <iostream>
using namespace std;

template <class T>
struct AVLNode
{
	AVLNode(const T& data)
		:_data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
	{}

	T _data;
	AVLNode<T>* _left;
	AVLNode<T>* _right;

	AVLNode<T>* _parent;
	int _bf;
};

template <class T>
class AVLTree
{
public:
	typedef AVLNode<T> Node;
	typedef Node* pNode;

	bool Insert(const T& data)
	{
		//判空
		if (_root == nullptr)
		{
			_root = new Node(data);
			return true;
		}

		//搜索
		pNode cur = _root;
		pNode parent = nullptr;
		while (cur)
		{
			parent = cur;
			if (cur->_data == data)
				return false;
			else if (cur->_data > data)
				cur = cur->_left;
			else
				cur = cur->_right;
		}

		//连接
		cur = new Node(data);
		if (parent->_data > data)
			parent->_left = cur;
		else
			parent->_right = cur;
		cur->_parent = parent;

		//调整
		while (parent)
		{
			//更新平衡因子
			//判断新插入的节点在左边还是右边
			if (parent->_right == cur)
			{
				++parent->_bf;
			}
			else
			{
				--parent->_bf;
			}

			if (parent->_bf == -1 || parent->_bf == 1)
			{
				//向上继续更新
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 0)
			{
				break;
			}
			else
			{
				//调整
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateRight(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateLeft(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					int bf = cur->_right->_bf;
					RotateLeft(cur);
					RotateRight(parent);
					if (bf == -1)
					{
						parent->_bf = 1;
					}
					else if (bf == 1)
						cur->_bf = -1;
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					int flag = cur->_left->_bf;
					RotateRight(cur);
					RotateLeft(parent);
					if (flag == -1)
					{
						cur->_bf = 1;
					}
					else if (flag == 1)
						parent->_bf = -1;
				}
				break;
			}
		}
		return true;
	}

	void RotateRight(pNode parent)
	{
		//右旋, 拿到左子树和左子树的右孩子
		pNode subL = parent->_left;
		pNode subLR = subL->_right;

		//改变链接关系
		subL->_right = parent;
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;
		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (parent->_parent->_left == parent)
			{
				parent->_parent->_left = subL;
			}
			else
				parent->_parent->_right = subL;
			subL->_parent = parent->_parent;
		}
		parent->_parent = subL;

		//更新平衡因子
		subL->_bf = parent->_bf = 0;
	}

	void RotateLeft(pNode parent)
	{
		//左旋
		pNode subR = parent->_right;
		pNode subRL = subR->_left;

		//改变链接关系
		subR->_left = parent;
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		if (parent == _root)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			pNode pparent = parent->_parent;
			if (pparent->_left == parent)
				pparent->_left = subR;
			else
				pparent->_right = subR;
			subR->_parent = pparent;
		}
		parent->_parent = subR;
		//更新平衡因子
		subR->_bf = parent->_bf = 0;
	}

	void Inorder()
	{
		_Inorder(_root);
		cout << endl;
	}
	void _Inorder(pNode root)
	{
		if (root)
		{
			_Inorder(root->_left);
			cout << root->_data << " ";
			_Inorder(root->_right);
		}
	}

	int _TreeHight(pNode root)
	{
		//空树为0
		if (root == nullptr)
			return 0;
		//计算左右子树
		int LeftHight = _TreeHight(root->_left);
		int RightHight = _TreeHight(root->_right);
		//返回较高子树的高度 + 1
		return LeftHight > RightHight ? (LeftHight + 1) : (RightHight + 1);
	}

	bool _IsBalance(pNode root)
	{
		//空树是平衡树
		if (root == nullptr)
			return true;
		//计算左右的高度差, 判断高度差是否满足, 不等于或者超出范围
		int LeftH = _TreeHight(root->_left);
		int RightH = _TreeHight(root->_right);
		int diff = RightH - LeftH;

		if (diff != root->_bf || (diff < -1 || diff > 1))
			return false;

		//返回左边和右边都平衡
		return _IsBalance(root->_left) && _IsBalance(root->_right);
	}
	bool IsBalance()
	{
		return _IsBalance(_root);
	}

private:
	pNode _root = nullptr;
};

void test()
{
	AVLTree<int> avl;
	int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
		avl.Insert(a[i]);

	avl.Inorder();
	cout << avl.IsBalance() << endl;
}

int main()
{
	test();
	system("pause");
	return 0;	
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值