AVLTree——高度平衡二叉搜索树

目录

一、AVLTree的引入

二、概念

1、概念

2、结点实现

 3、整体框架

三、新节点的插入

1、插入

2、平衡因子更新规则

四、旋转调平衡

1、左单旋

2、右单旋

3、左右双旋

4、右左双旋

五、总代码

1、AVLTree.h

2、test.cpp


一、AVLTree的引入

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法——AVLTree。

AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。


二、概念

1、概念

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
1、它的左右子树都是AVL树。
2、左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)。
3、平衡因子 = 右子树高度 - 左子树高度。

2、结点实现

template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	int _bf; //平衡因子 balance factor

	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _bf(0)
	{}
};

 3、整体框架

template<class K, class V>
struct AVLTree
{
	typedef AVLTreeNode<K, V> Node;

private:
	Node* _root = nullptr;
}

三、新节点的插入

1、插入

因为AVL树是一颗搜索二叉树,所以它的插入和我们前面讲到的搜索二叉树的插入一样,我们都是小的往左子树插入,大的往右子树插入。代码如下:

    bool insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		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;

		//调整平衡
		// 1、更新平衡因子
		while (parent)
		{
			if (cur == parent->_right)
			{
				parent->_bf++;
			}
			else
			{
				parent->_bf--;
			}

			if (parent->_bf == 0)
			{
				break;
			}
			else if (abs(parent->_bf) == 1)
			{
				parent = parent->_parent;
				cur = cur->_parent;
			}
			else if (abs(parent->_bf) == 2)
			{
				// 说明parent 所在的子树不平衡了,需要旋转处理
				// 1、单旋
				if (parent->_bf == 2 && cur->_bf == 1) // 左单旋
				{
					RotateL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == -1) //右单旋
				{
					RotateR(parent);
				}
				//2、双旋
				else if (parent->_bf == -2 && cur->_bf == 1)// 左右双旋
				{
					RotateLR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1) //右左双旋
				{
					RotateRL(parent);
				}
				else
				{
					assert(false);
				}
				break;
			}
			else
			{
				assert(false);
			}
		}

		return true;
	}

我们在插入一个新的结点后,先要更新平衡因子。而插入新节点后,AVL树可能不会平衡了,所以我们必须根据平衡因子去判断它是否平衡,如果不平衡,我们就需要进行旋转调整。

2、平衡因子更新规则

1、新增在右,parent->bf++,新增在左,parent->bf--。

2、更新后,parent->bf == 1 or -1,说明parent插入前的平衡因子是0,且左右子树高度相等,插入后有一边高,parent高度变了,需要继续往上更新

3、更新后,parent->bf == 0,说明parent插入前的平衡因子是1 or -1,说明左右子树一边高一边低,插入后两边一样高,插入填上了矮的那边,parent所在子树高度不变,不需要往上继续更新

4、更新后,parent->bf == 2 or -2,说明parent插入之前的平衡因子是1 or -1,已是平衡临界值,插入变成 2 or -2,打破平衡,parent所在子树需要旋转处理。

上面的代码中有四个函数涉及到旋转调节平衡。那么下面我们就来详细讲解一下四种情况的旋转调节AVL树的平衡。


四、旋转调平衡

1、左单旋

我们在出现了下面的情况后就必须对树进行左单旋。即新节点插入较高右子树的右侧——左单旋。

一般步骤:

1、subRL成为parent的右子树,然后要更新subRL的父亲为parent,更新parent的右为subRL(注:subRL可能为空)。

2、parent成为subR的左子树,更新parent的父亲为subR,而subR的左为parent。

3、注意parent是不是整棵树的root,如果是,则让subR成为_root,同时让_root->_parent置为空;如果不是,就将subR连接到它父亲的左或者右。最后更新平衡因子。

    void RotateL(Node* parent) //左单旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* pparent = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;
		
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

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

			subR->_parent = pparent;
		}
		parent->_bf = subR->_bf = 0;
	}

2、右单旋

新节点插入较高左子树的左侧——右单旋。

一般步骤:

1、subLR成为parent的左子树,然后要更新subLR的父亲为parent,更新parent的左为subLR(注:subRL可能为空)。

2、parent成为subR的右子树,更新parent的父亲为subL,而subL的右为parent。

3、注意parent是不是整棵树的root,如果是,则让subL成为_root,同时让_root->_parent置为空;如果不是,就将subL连接到它父亲的左或者右。最后更新平衡因子。

    void RotateR(Node* parent) //右单旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* pparent = parent->_parent;

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

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

			subL->_parent = pparent;
		}
		subL->_bf = parent->_bf = 0;
	}

3、左右双旋

新节点插入较高左子树的右侧——先左单旋再右单旋。

一般步骤:

1、先调用左单旋函数,对parent的左进行左单旋。

2、再调用右单旋函数,对parent进行右单旋。

3、根据原来的subLR的平衡因子的大小,来更新parent和subL的平衡因子。

    void RotateLR(Node* parent) //左右双旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

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

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

4、右左双旋

新节点插入较高右子树的左侧——先右单旋再左单旋。

一般步骤:

1、先调用右单旋函数,对parent的右进行右单旋。

2、再调用左单旋函数,对parent进行左单旋。

3、根据原来的subRL的平衡因子的大小,来更新parent和subR的平衡因子。

    void RotateRL(Node* parent) //右左双旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

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

		if (bf == 1)
		{
			subRL->_bf = 0;
			parent->_bf = -1;
			subR->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			subR->_bf = 1;
			subRL->_bf = 0;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			subR->_bf = 0;
			subRL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}


五、AVL树的验证

首先我们验证它是一棵二叉搜索树,如果一棵树的中序遍历的结果是有序的,那么他就是一棵二叉搜索树。用下面的代码:

void _InOrder(Node* root)
{
	if (root == nullptr)
		return;

	_InOrder(root->_left);
	cout << root->_kv.first << ":" << root->_kv.second << endl;
	_InOrder(root->_right);
}

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

一棵树在满足它是一棵二叉搜索树后,再去判断它是否是AVL树。

    bool IsBalance()
	{
		return _IsBalance(_root);
	}

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

		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);

		return max(leftHeight, rightHeight) + 1;
	}

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

		int left = Height(root->_left);
		int right = Height(root->_right);
		int diff(right - left);

		if (diff != root->_bf)
		{
			cout << root->_kv.first << "平衡因子异常" << endl;
		}

		return abs(diff) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}

 验证结果代码:

void test_AVLTree2()
{
	//测试双旋平衡因子的调节
	int b[] = { 4,2,6,1,3,515,7,16,14 };
	AVLTree<int, int> t;
	for (auto e : b)
	{
		t.insert(make_pair(e, e));
	}
	t.InOrder();
	cout << "IsBalance: " << t.IsBalance() << endl;
}

运行结果:


六、总代码

1、AVLTree.h

#pragma once
#include<iostream>
#include<cassert>
#include<cstdbool>
#include<algorithm>
using namespace std;

//高度平衡二叉搜索树
template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	int _bf; //平衡因子 balance factor

	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _bf(0)
	{}
};

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

		Node* parent = nullptr;
		Node* cur = _root;
		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;

		//调整平衡
		// 1、更新平衡因子
		while (parent)
		{
			if (cur == parent->_right)
			{
				parent->_bf++;
			}
			else
			{
				parent->_bf--;
			}

			if (parent->_bf == 0)
			{
				break;
			}
			else if (abs(parent->_bf) == 1)
			{
				parent = parent->_parent;
				cur = cur->_parent;
			}
			else if (abs(parent->_bf) == 2)
			{
				// 说明parent 所在的子树不平衡了,需要旋转处理
				// 1、单旋
				if (parent->_bf == 2 && cur->_bf == 1) // 左单旋
				{
					RotateL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == -1) //右单旋
				{
					RotateR(parent);
				}
				//2、双旋
				else if (parent->_bf == -2 && cur->_bf == 1)// 左右双旋
				{
					RotateLR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1) //右左双旋
				{
					RotateRL(parent);
				}
				else
				{
					assert(false);
				}
				break;
			}
			else
			{
				assert(false);
			}
		}

		return true;
	}

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

	bool IsBalance()
	{
		return _IsBalance(_root);
	}

private:
	void RotateL(Node* parent) //左单旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* pparent = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;
		
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

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

			subR->_parent = pparent;
		}
		parent->_bf = subR->_bf = 0;
	}

	void RotateR(Node* parent) //右单旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* pparent = parent->_parent;

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

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

			subL->_parent = pparent;
		}
		subL->_bf = parent->_bf = 0;
	}

	void RotateLR(Node* parent) //左右双旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

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

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

	void RotateRL(Node* parent) //右左双旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

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

		if (bf == 1)
		{
			subRL->_bf = 0;
			parent->_bf = -1;
			subR->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			subR->_bf = 1;
			subRL->_bf = 0;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			subR->_bf = 0;
			subRL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}

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

		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);

		return max(leftHeight, rightHeight) + 1;
	}

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

		int left = Height(root->_left);
		int right = Height(root->_right);
		int diff(right - left);

		if (diff != root->_bf)
		{
			cout << root->_kv.first << "平衡因子异常" << endl;
		}

		return abs(diff) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}
	
private:
	Node* _root = nullptr;
};

void test_AVLTree1()
{
	int a[] = { 16,3,7,11,9,26,18,14,15 };
	AVLTree<int, int> t1;
	for (auto e : a)
	{
		t1.insert(make_pair(e, e));
	}
	t1.InOrder();
	cout << "IsBalance: " << t1.IsBalance() << endl;
}

void test_AVLTree2()
{
	//测试双旋平衡因子的调节
	int b[] = { 4,2,6,1,3,515,7,16,14 };
	AVLTree<int, int> t;
	for (auto e : b)
	{
		t.insert(make_pair(e, e));
	}
	cout << "IsBalance: " << t.IsBalance() << endl;
}

2、test.cpp

#include"AVLTree.h"

int main()
{
	test_AVLTree1();
	cout << "——————————————" << endl;
	test_AVLTree2();

	return 0;
}

运行结果:

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值