带你深入理解AVL(平衡二叉树)



一、AVL tree 的概述

AVL Tree 是一个“加上了额外的平衡条件的二叉搜索树”。其平衡条件的建立是为了确保整棵树的深度为O(logN)。直观上的最佳平衡条件是每个节点的左右子树有相同的高度,但这太严苛,我们很难插入新元素而又保持这样的平衡条件。AVL Tree 于是退而求其次,要求任何节点的左右子树高度相差最多1。这是一个较弱的条件,但仍能够保证“对数深度”平衡状态。

如下图所示的是一个AVL Tree,插入了节点11之后,灰色节点违法了AVL Tree的平衡条件。由于只有“插入点至根节点”路径上的各节点可能改变平衡状态,因此,只要调整其中最深的那个节点,便可使整棵树重获平衡。

在这里插入图片描述

前面说过,只要调整“插入点至根节点”路径上,平衡状态被破坏之各节点中最深的那一个,便可以使整棵树重新获得平衡。假设该最深节点为X,由于节点最多拥有两个节点,而所谓“平衡被破坏”意味着X的左右两颗子树的高度相差2,因此我们可以轻易将情况分为四种。

  • 插入点位于X的左子节点的左子树——左左
  • 插入点位于X的左子节点的右子树——左右
  • 插入点位于X的右子节点的左子树——右左
  • 插入点位于X的右子节点的右子树——右右

在这里插入图片描述

二、AVL Tree的平衡操作

1.单旋转

在外侧插入状态中,k2“插入前平衡,插入后不平衡”的唯一情况如下图左侧所示:

在这里插入图片描述

为了调整平衡状态,我们希望将A子树提高一层,并将C子树下降一层,如上图右侧所示。我们可以这样想象,把k1向上提起,使k2自然下滑,并将B子树挂到k2的左侧。这么做是因为,二叉搜索树的规则使我们知道,k2>k1,所以k2必须成为新树形中的k1的右子节点。二叉搜索树的规则也告诉我们,B子树的所有节点的键值都在k1和k2之间,所以新树形中的B子树必须落在k2的左侧。

2.双旋转

下图左侧为内侧插入所造成的不平衡状态。单旋转无法解决这种情况。第一,我们不能再以k3为根节点,其次,我们不能将k3和k1做一次单旋转,因为旋转后还是不平衡。唯一的可能是以k2为新的根节点,这使得k1必须成为k2的左子节点,k3必须成为k2的右子节点,而这么一来也就完全决定了四个子树的位置。新的树形满足AVL Tree的平衡条件,并且,就像单旋转的情况一样,它恢复了节点插入之前的高度,因此不需要任何调整。

在这里插入图片描述

下图是两次单旋转的过程:

在这里插入图片描述


三、AVL Tree的代码实现

1.节点结构

这里我们引入了平衡因子的概念,平衡因子 = 右子树高度 - 左子树高度 ,一旦一个节点的平衡因子大于1或者小于-1,则说明以该节点为root结点的子树不平衡,也就是上述所说的需要调整的X节点。

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;//平衡因子

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

2.根据平衡因子判断如何旋转

a).左左——右单旋

在这里插入图片描述

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

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

		Node* ppnode = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;

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

			subL->_parent = ppnode;
		}

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

b).右右——左单旋

在这里插入图片描述

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

		parent->_right = subRL;
		//判断subRL是否为空,不为空才能链接其父节点
		if(subRL) subRL->_parent = parent;

		//记录parent的上一个结点,后续需要调整平衡因子
		Node* ppnode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;

		//parent可能是一个树的根节点,也可能是一颗子树
		if (ppnode == nullptr) { _root = subR; _root->_parent = nullptr; }
		else
		{
			if (ppnode->_right == parent) ppnode->_right = subR;
			else ppnode->_left = subR;

			subR->_parent = ppnode;
		}

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

c).左右——先左单旋后右单旋

在这里插入图片描述

对于双旋,涉及到的节点多,所以平衡因子的调整更为复杂

void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		int bf = subLR->_bf;
		RotateL(parent->_left);
		RotateR(parent);

		if (bf == -1)//subLR的左树新增结点
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)//subLR的右树新增结点
		{
			parent->_bf = 0;
			subL->_bf = -1;
			subLR->_bf = 0;
		}
		else if (bf == 0)//subLR新增
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 0;
		}
		else assert(false);
	}

d).右左——先右单旋再左单旋

在这里插入图片描述

void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		int bf = subRL->_bf;
		//RotateL(subR);
		//RotateR(subRL);
		RotateR(parent->_right);
		RotateL(parent);

		if (bf == 1)//subRL的右节点新增
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else assert(false);

	}

3.代码实现

AVL Tree 的实现如下,我们还写了一段测试一棵树的代码是否是AVL Tree 的代码:

#pragma once
#include<assert.h>
#include<iostream>
#include<vector>
#include<cmath>
#include<utility>
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;//平衡因子

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

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

private:
	Node* _root = nullptr;

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

		//更新平衡因子
		while (parent)
		{
			if (parent->_left == cur)  parent->_bf--;
			else  parent->_bf++;

			//调平衡
			//1.parent->_bf == 0 ,即插入之前_bf == 1 或者 -1 ,不需要继续更新 
			//2.parent->_bf == 1 或者 -1,即插入之前_bf == 0 ,高度变换了,需要继续往上更新
			//3.parnet->_bf == 2 或者 -1,即插入之前_bf == 1 或者 -1 ,需要调整
			if (parent->_bf == 0) break;
			else if (parent->_bf == 1 || parent->_bf == -1) { cur = parent; parent = parent->_parent; }
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				//旋转
				//1.左单旋
				//2.右单旋
				//3.左右双旋
				//4.右左双旋

				if (parent->_bf == 2 && cur->_bf == 1)   RotateL(parent);
				else if (parent->_bf == -2 && cur->_bf == -1)  RotateR(parent);
				else if (parent->_bf == 2 && cur->_bf == -1) RotateRL(parent);
				else if (parent->_bf == -2 && cur->_bf == 1) RotateLR(parent);
				else assert(false);
				break;
			}
			else assert(false);
		}
		return true;
	}


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

		parent->_right = subRL;
		//判断subRL是否为空,不为空才能链接其父节点
		if(subRL) subRL->_parent = parent;

		//激励parent的上一个结点,后续需要调整平衡因子
		Node* ppnode = parent->_parent;
		subR->_left = parent;
		//subR->_parent = parent->_parent;
		parent->_parent = subR;

		//parent可能是一个树的根节点,也可能是一颗子树
		if (ppnode == nullptr) { _root = subR; _root->_parent = nullptr; }
		else
		{
			//if (ppnode->_right == subR) ppnode->_right = subR;--------------my bug
			if (ppnode->_right == parent) ppnode->_right = subR;
			else ppnode->_left = subR;

			subR->_parent = ppnode;
		}

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

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

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

		Node* ppnode = parent->_parent;
		subL->_right = parent;
		//不论parent->_parent是否为空都可以链接
		//subL->_parent = parent->_parent;
		parent->_parent = subL;

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

			subL->_parent = ppnode;
		}

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


	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		int bf = subLR->_bf;
		//RotateL(subL);
		//RotateR(subLR);
		RotateL(parent->_left);
		RotateR(parent);

		if (bf == -1)//subLR的左树新增结点
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)//subLR的右树新增结点
		{
			parent->_bf = 0;
			subL->_bf = -1;
			subLR->_bf = 0;
		}
		else if (bf == 0)//subLR新增
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 0;
		}
		else assert(false);
	}

	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		int bf = subRL->_bf;
		//RotateL(subR);
		//RotateR(subRL);
		RotateR(parent->_right);
		RotateL(parent);

		if (bf == 1)//subRL的右节点新增
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else assert(false);

	}

	void Inorder() { _Inorder(_root); }
	bool isBalance() { return _isBalance(_root); }

	int Height(Node* root)
	{
		if (root == nullptr) return 0;
		int leftH = Height(root->_left);
		int rightH = Height(root->_right);

		return leftH > rightH ? leftH + 1 : rightH + 1;
	}


private:
	void _Inorder(Node* root)
	{
		if (root == nullptr) return;

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

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

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

		if (right - left != root->_bf) { cout << "_bf error!\n"; return false; }
		return abs(right - left) < 2 && _isBalance(root->_left) && _isBalance(root->_right);
	}


};

void TestAVLTree()
{
	srand(time(0));
	const size_t N = 10000;
	AVLTree<int, int> t;

	for (size_t i = 0; i < N; ++i)
	{
		size_t x = rand();
		t.Insert(make_pair(x, x));
		//cout << t.IsBalance() << endl;
	}

	t.Inorder();

	//vector<int> arr = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	vector<int> arr = { 16, 3, 7, 11, 9, 26 };
	//AVLTree<int, int> t;
	//for (int i = 0; i < arr.size(); i++)
	//{
	//	t.Insert(make_pair(arr[i], arr[i]));
	//}
	//t.Inorder();
	
	cout <<	t.isBalance() << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值