C++ AVL树

14 篇文章 0 订阅
本文详细阐述了AVL树的概念,强调了其通过维持节点左右子树高度差不超过1来保持高效搜索。文章介绍了AVL树的插入过程,包括遵循二叉搜索树规则插入新节点并调整平衡因子,以及删除节点后的平衡因子更新和可能的旋转操作。
摘要由CSDN通过智能技术生成

1.概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。

因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年发明了一种解决上述问题的方法:

当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1),如下图:

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

2.AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。

平衡因子(_bf)就是该节点右子树高度减去左子树高度

那么 AVL树的插入过程可以分为两步:

1. 按照二叉搜索树的方式插入新节点

2. 调整节点的平衡因子

调整节点的平衡因子

一个合格的AVL树在插入之前,就应该满足AVL树的条件,插入节点的父亲节点的平衡因子只有三种可能(-1,0,1)

插入分以下几种情况:

第一种和第四种插入后子树的高度没有变化,所以上面的节点的平衡因子都不会变,平衡因子的更新可以结束

第二种和第三种插入后子树的高度发生变化,所以上面的节点的平衡因子要变,要继续更新平衡因子

继续更新后如果有一个节点的平衡因子不再是(-1,0,1)也就是变成2或者-2,就要进行处理

平衡因子变成2或者-2的节点的子树,对于这个子树,一定是高的那个子树插入一个节点,导致在原先就高的那一边的子树,更加高了

我们可以分成以下几种情况继续分析

2 1树(左旋)

平衡因子不合格的本质是树的高度差太大,所以需要降高度。

对8左旋:

-2 -1树(右旋)

对8右旋:

-2 1树(左右旋)

先对8左旋再对10右旋:

这里加在b,也可以加在c,还有可能9就是新增节点,但是中间旋转的过程都是不变的,改变的只有平衡因子,但顶部的平衡因子一定为0。

2 -1 树(右左旋)

先对10右旋再对8左旋:

和上面一样,这里加在c,也可以加在b,还有可能9就是新增节点,但是中间旋转的过程都是不变的,改变的只有平衡因子,但顶部的平衡因子一定为0。

3.AVL树的删除

删除我熬了一个通宵搞明白的,不容易啊

3.1.删除节点

先考虑一下,删除有哪些情况:

1.删除的节点左右都为空,删除的就是叶子节点

2.删除的节点左右一边为空,只有以下两种情况,因为这是AVL树,所有节点的平衡因子只能是(-1,0,1)

现在已知一边为空,所以平衡因子=另一边子树的高度,所以只能有一个节点

3.删除的节点的左右都都不为空,这种情况,和搜索二叉树的找月嫂思想是一样的

这三种情况的解决方式有异曲同工之处

情况1:删除叶子节点,如果他是父亲的左孩子,(bf)平衡因子++,反之--

情况2:可以把被删除的节点存储的值和他的唯一的孩子交换,然后删除他的孩子,问题转换成情况1

情况3:找到左子树的最大值节点,与其交换一下存储的内容,问题转换成删除那个左子树最大的节点,这个节点,又符合左为空的情况,如果右也为空就是情况1,反之情况2

删除节点最后都会转为删除叶子节点

3.2.平衡因子更新

节点删除了,被删除节点的父亲节点的平衡因子也更新了,但是是否需要继续更新,当前位置是否平衡都需要考虑

平衡因子的正常范围是[-1,1]

被删除节点的父亲节点平衡因子可以分为以下三种情况:

1.bf == 0 

说明原先该节点的bf == -1 或者 1 变成 0 后  说明原先高的那一边被删除了,导致高的子树高度降低,变得和另一边一样高,整颗树树的高度发生了变化,需要继续更新

2.bf == 1 或者 -1

说明原先该节点的bf == 0 0 变成 1 后 说明原先平衡,删除了一边,导致一边子树的高度变低了,但整颗树的高度没有变化,不需要更新

3. bf == 2 或者 bf == -2

这颗子树不平衡了,需要处理

3.3.旋转

不平衡分成两种大情况:

1.由于删除节点,导致当前子树不平衡

2.由于删除节点后导致平衡因子更新,而导致树不平衡

在更新平衡因子过程中,cur的bf一定是变成0,才会进一步,导致parent变化

在这个过程中,parent的bf变成2或者-2,是判断是否需要旋转的条件

而具体该怎么旋转,就由叔叔决定,因为cur的bf一定是0。和上面插入的旋转道理是一样的,也是这四种旋转。

这里右六种情况:

因为parent的bf可以是-2或者2,uncle的bf可以是-1,0,1

parent->bfuncle->bf旋转方式
21左旋
20左旋
2-1右左双旋
-21右旋
-20右旋
-2-1左右双旋

在旋转之后还需要更新cur和parent,因为还有继续更新平衡因子的需要

4.源代码

#pragma once
#include<assert.h>

template<class K, class V>
struct AVLtreeNode
{
	AVLtreeNode* _left;
	AVLtreeNode* _right;
	AVLtreeNode* _parent;
	pair<K, V> _kv;
	int _bf;

	AVLtreeNode(const K& key, const V& val)
		:_left(nullptr),
		_right(nullptr),
		_parent(nullptr),
		_kv(key, val),
		_bf(0)
	{}
};

template<class K, class V>
class AVLtree
{
public:
	typedef AVLtreeNode<K, V> Node;
	typedef pair<K, V> value_type;
	AVLtree() :_root(nullptr){}

	bool insert(const value_type& val)
	{
		if (_root == nullptr)
		{
			_root = new Node(val.first, val.second);
			return true;
		}
		// 找插入位置
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			parent = cur;
			if (cur->_kv.first > val.first)
				cur = cur->_left;
			else if (cur->_kv.first < val.first)
				cur = cur->_right;
			else
				return false;
		}
		// 插入
		if (parent->_kv.first > val.first)
		{
			parent->_left = new Node(val.first, val.second);
			cur = parent->_left;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = new Node(val.first, val.second);
			cur = parent->_right;
			cur->_parent = parent;
		}
		// 更新_bf平衡因子
		while (parent)
		{
			if (cur == parent->_left)
				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 || parent->_bf == 2)
			{
				//不平衡,要旋转
				if (parent->_bf > 0 && cur->_bf > 0)
					RotateL(parent);
				else if (parent->_bf < 0 && cur->_bf < 0)
					RotateR(parent);
				else if (parent->_bf > 0 && cur->_bf < 0)
					RotateRL(parent);
				else
					RotateLR(parent);
				
				break;
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}

	bool erase(const K& key)
	{
		if (_root == nullptr)
		{
			return false;
		}
		
		//找key
		Node* cur = _root;
		while(cur)
		{
			if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else//找到了
			{
				if (cur->_left == nullptr && cur->_right == nullptr)//删除的节点是叶子节点
				{
					cur = DeleteNode(cur);
				}
				else if (cur->_left == nullptr)
				{
					Node* del = cur->_right;
					cur->_kv = del->_kv;
					cur = DeleteNode(del);
				}
				else if (cur->_right == nullptr)
				{
					Node* del = cur->_left;
					cur->_kv = del->_kv;
					cur = DeleteNode(del);
				}
				else
				{
					//找左子树的最大节点
					Node* leftmax = cur->_left;
					while (leftmax->_right)
						leftmax = leftmax->_right;

					cur->_kv = leftmax->_kv;

					//是否需要托孤
					if(leftmax->_left)
					{
						Node* del = leftmax->_left;
						leftmax->_kv = del->_kv;
						cur = DeleteNode(del);
					}
					else
					{
						Node* del = leftmax;
						cur = DeleteNode(del);
					}
				}
				if (cur == nullptr)//根节点被删除
					return true;
				if (cur->_bf == 2 || cur->_bf == -2)//删除那个节点导致不平衡
				{
					Node* parent = cur;
					cur = nullptr;
					Rotate_delete_check(parent, cur);
				}
				
				//更新平衡因子
				Node* parent = cur->_parent;
				while (parent && cur->_bf == 0)//cur的平衡因子是0才需要更新
				{
					if (parent->_left == cur)
						parent->_bf++;
					else
						parent->_bf--;

					if (parent->_bf == -1 || parent->_bf == 1)
					{
						//无需继续更新
						break;
					}
					else if (parent->_bf == 0)
					{
						//继续更新
						cur = parent;
						parent = parent->_parent;
					}
					else if (parent->_bf == -2 || parent->_bf == 2)
					{
						cout << "//" << endl;
						Rotate_delete_check(parent, cur);
					}
				}
				return true;
			}
		}
		return false;
	}

	bool check_balance()
	{
		return _check_balance(_root);
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

private:

	Node* DeleteNode(Node* cur)//删除叶子节点,并且返回其父亲
	{
		Node* del = cur;
		Node* parent = cur->_parent;
		if (parent == nullptr)//cur是根节点
		{
			_root = nullptr;
			delete del;
			return nullptr;
		}
		if (parent->_left == del)
		{
			parent->_left = nullptr;
			parent->_bf++;
		}
		else
		{
			parent->_right = nullptr;
			parent->_bf--;
		}
		delete del;
		return parent;
	}

	void Rotate_delete_check(Node*& parent, Node*& cur)
	{
		Node* uncle;
		if (parent->_left == cur)
			uncle = parent->_right;
		else
			uncle = parent->_left;

		if (parent->_bf == 2 && uncle->_bf == 1)
		{
			RotateL(parent);
			cur = uncle;
		}
		else if (parent->_bf == -2 && uncle->_bf == -1)
		{
			RotateR(parent);
			cur = uncle;
		}
		else if (parent->_bf == 2 && uncle->_bf == -1)
		{
			cur = uncle->_left;
			RotateRL(parent);
		}
		else if (parent->_bf == -2 && uncle->_bf == 1)
		{
			cur = uncle->_right;
			RotateLR(parent);
		}
		else if (parent->_bf == 2 && uncle->_bf == 0)
		{
			RotateL(parent);
			parent->_bf = 1;
			uncle->_bf = -1;
			cur = uncle;
		}
		else if (parent->_bf == -2 && uncle->_bf == 0)
		{
			RotateR(parent);
			parent->_bf = -1;
			uncle->_bf = 1;
			cur = uncle;
		}
		else assert(false);
		parent = cur->_parent;
	}

	void balence_check(Node* parent, Node* cur)
	{
		if (parent == nullptr)
			return;
		if (parent->_bf == 0)
		{
			//无需继续更新
		}
		else if (parent->_bf == -2 || parent->_bf == 2)
		{
			//不平衡,要旋转
			if (parent->_bf > 0 && cur->_bf > 0)
				RotateL(parent);
			else if (parent->_bf < 0 && cur->_bf < 0)
				RotateR(parent);
			else if (parent->_bf > 0 && cur->_bf < 0)
				RotateRL(parent);
			else
				RotateLR(parent);
		}
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_kv.first << " ";
		_InOrder(root->_right);
	}

	bool _check_balance(Node* root)
	{
		if (root == nullptr)
			return true;

		int lefthight = TreeHight(root->_left);
		int righthight = TreeHight(root->_right);
		if (abs(lefthight - righthight) >= 2 || righthight - lefthight != root->_bf)
		{
			cout << root->_kv.first << ":" << root->_bf << endl;
			return false;
		}
		return _check_balance(root->_left) && _check_balance(root->_right);
	}

	int TreeHight(Node* root)
	{
		if (root == nullptr)
			return 0;

		int lefthight = TreeHight(root->_left);
		int righthight = TreeHight(root->_right);
		return lefthight > righthight ? lefthight + 1: righthight + 1;
	}


	void RotateL(Node* parent)//左单旋
	{
		Node* Rightson = parent->_right;

		//旋转节点之间的链接
		parent->_right = Rightson->_left;
		if (parent->_right)
			parent->_right->_parent = parent;
		Rightson->_left = parent;
		Node* pparent = parent->_parent;
		parent->_parent = Rightson;
		
		//更新祖先的链接
		if (pparent == nullptr)
		{
			Rightson->_parent = nullptr;
			_root = Rightson;
		}
		else
		{
			if (pparent->_kv.first > Rightson->_kv.first)
				pparent->_left = Rightson;
			else
				pparent->_right = Rightson;
			Rightson->_parent = pparent;
		}
		parent->_bf = Rightson->_bf = 0;
	}

	void RotateR(Node* parent)//右单旋
	{
		Node* Leftson = parent->_left;

		//旋转节点之间的链接
		parent->_left = Leftson->_right;
		if (parent->_left)
			parent->_left->_parent = parent;
		Leftson->_right = parent;
		Node* pparent = parent->_parent;
		parent->_parent = Leftson;

		//更新祖先的链接
		if (pparent == nullptr)
		{
			Leftson->_parent = nullptr;
			_root = Leftson;
		}
		else
		{
			if (pparent->_kv.first > Leftson->_kv.first)
				pparent->_left = Leftson;
			else
				pparent->_right = Leftson;
			Leftson->_parent = pparent;
		}
		parent->_bf = Leftson->_bf = 0;
	}

	void RotateLR(Node* parent)
	{
		Node* Leftson = parent->_left;
		Node* LRightsson = Leftson->_right;
		int bf = LRightsson->_bf;

		RotateL(Leftson);
		RotateR(parent);

		//平衡因子更新
		if (bf == 1)
		{
			Leftson->_bf = -1;
		}
		else if (bf == -1)
		{
			parent->_bf = 1;
		}
		else if (bf == 0) {}
		else
		{
			assert(false);
		}
	}

	void RotateRL(Node* parent)
	{
		Node* Rightson = parent->_right;
		Node* RLeftsson = Rightson->_left;
		int bf = RLeftsson->_bf;

		RotateR(Rightson);
		RotateL(parent);

		//平衡因子更新
		if (bf == 1)
		{
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			Rightson->_bf = 1;
		}
		else if(bf == 0){}
		else
		{
			assert(false);
		}
	}

private:
	Node* _root;
};

写完了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值