手撕AVL树

目录

1.概念

1.1 二叉搜索树的缺点

2.2 定义节点

2.插入

2.1 拆分

2.2 找节点与插节点

更新平衡因子

旋转

(1)2和1,触发左旋

(2)-2和-1,触发右旋

(3)-2和1,左右双旋

(4)2和-1,右左双旋

3.判断

4.完整代码及测试代码

1.概念

1.1 二叉搜索树的缺点

AVL树是在二叉搜索树的基础上进行改造的。当我们插入的是一个有序的序列的时候,二叉搜索树会使用一条直线来进行存储,这样并不利于查找。

当遇到这种情况的时候我们就需要对这棵树来进行调整。AVL树会通过旋转等操作,来规避这种请情况。最终满足每一个节点的平衡因子的绝对值<=1,从而达到近似平衡的目的。

节点的平衡因子值=右子树的高度-左子树高度

2.2 定义节点

在AVL树中,除了需要定义平衡因子bf之外,还需要定义指向节点父节点的指针。方便我们来进行平衡因子的更新。

template <class K,class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* right;
	AVLTreeNode<K, V>* left;
	AVLTreeNode<K, V>* parent;
	pair<K, V> _kv;
	int _bf;

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

同时和map一样,我们使用pair类型来进行数据的存储。

2.插入

2.1 拆分

AVL树的插入就是AVL树的精髓所在,我们在插入节点的同时还需要对平衡因子进行控制。

AVL树的插入我们可以拆分成五个函数,其中四个为旋转函数,一个为主要的插入函数。

而这个主要的插入函数,我们还可以将其分为三个部分:找节点,插节点,更新平衡因子。而更新平衡因子后就需要判断是否需要进行旋转的操作。

在进行插入之前,我们将插入的节点定义为kv。

2.2 找节点与插节点

这一过程与二叉搜索树是相同的。

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 (kv.first < cur->_kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (kv.first > cur->_kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			return false;
		}
	}
	//插入节点
	cur = new Node(kv);
	if (kv.first < parent->_kv.first)
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}
	cur->_parent = parent;

2.3 找节点与插节点

更新平衡因子

每当我们插入一个节点的时候,就需要对该节点的所有父辈节点来进行平衡因子的更新。注意,当插入节点后,只有父辈节点的平衡因子才会受到影响,而其他节点的平衡因子不会被影响。

可以根据每个节点的parent来找到其父亲节点,从而逐渐向上更新平衡因子。

当遇到以下两种情况平衡因子的更新停止。

1.某一父辈节点的平衡因子为0。

2.更新到根节点。

旋转

当更新之后的平衡因子为2或者-2的时候,不符合AVL树的平衡因子在-1~1之间的定义,此时需要发生旋转。触发旋转的条件与当前节点cur和它的parent有关。

当parent和cur的平衡因子分别为:

(1)2和1,触发左旋
void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	Node* ppNode = parent->_parent;

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

	subR->_left = parent;
	parent->_parent = subR;

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

用一张图来表示一下这个过程:

h表示某树的高度,当红色部分插入一个节点时,60的平衡因子变成1,30的平衡因子变为2。

此时就需要发生旋转:

通过旋转使树重新变成一颗AVL树,整个过程分为三步:

1.60的左子树置为30,30的右子树置为60的左子树。

2.将30与更上层的父辈节点链接起来。

3.将30和60的平衡因子都更新为0。

注意:由于AVL树时三叉树,因此在链接的时候需要将父节点也链接起来。因此在将60的左子树链接到30的右子树的时候,需要进行判空来避免空指针的解引用。

(2)-2和-1,触发右旋

右旋同样分为两步:

1.将30的有链接到60的左子树,将60链接到30的右。

2.将30与上层节点链接起来。

3.将30和60的平衡因子都更新为0。

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

	parent->_left = subLR;
	if (subLR)
		subLR->_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;
	}
	parent->_bf = subL->_bf = 0;
}
(3)-2和1,左右双旋

当为-2和1或者2和-1的时候,仅仅靠单旋是解决不了问题的,这个时候我们就需要进行双旋:

左单选:

右单旋:

无论是在哪一个红色部分插入节点,都会导致发生左右双旋。

左右双旋分为三步:

1.对30节点进行左单旋。

2.对90节点进行右单旋。

3.根据60的平衡因子来更新30和90的平衡因子

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

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

	if (bf == 0)
	{
		subLR->_bf = 0;
		subL->_bf = 0;
		parent->_bf = 0;
	}
	else if (bf == -1)
	{
		subL->_bf = 0;
		parent->_bf = 1;
		subLR->_bf = 0;
	}
	else if (bf == 1)
	{
		subL->_bf = -1;
		parent->_bf = 0;
		subLR->_bf = 0;
	}
	else
	{
		assert(false);
	}
}
(4)2和-1,右左双旋

右旋:

左旋:

无论在那个红色部分插入节点都会导致发生右左双旋。

右左双旋分为三步:

1.对90节点进行右单旋

2.对30节点进行左单选

3.根据60的平衡因子来更新30和90的平衡因子

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

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

3.判断

我们可以建立一个函数来判断一棵树是否为AVL树。

我们使用递归来进行这一过程,依次判断各个子树是否为AVL树。

要判断我们就需要知道每一棵树的高度,此时我们需要构造一个求树的高度的函数Height,他也由递归来实现。

	int Height(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		int leftHeight = Height(root->left);
		int rightHeight = Height(root->right);
		return rightHeight > leftHeight ? rightHeight + 1 : leftHeight + 1;
	}
	bool IsBalance()
	{
		return _IsBalance(_root);
	}
	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		int leftHeight = Height(root->left);
		int rightHeight = Height(root->right);
		if ((rightHeight - leftHeight) != root->_bf)
		{
			cout << "现在是:" << root->_bf << endl;
			cout << "应该是:" << rightHeight - leftHeight << endl;
			return false;
		}
		return abs(rightHeight - leftHeight) < 2 && _IsBalance(root->left) && _IsBalance(root->right);
	}

4.完整代码及测试代码

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

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

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

template <class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* ppNode = parent->_parent;

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

		subR->_left = parent;
		parent->_parent = subR;

		if (parent == _root)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
			subR->_parent = ppNode;
		}
		parent->_bf = subR->_bf = 0;
	}
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* ppNode = parent->_parent;

		parent->_left = subLR;
		if (subLR)
			subLR->_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;
		}
		parent->_bf = subL->_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 == 0)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == -1)
		{
			subL->_bf = 0;
			parent->_bf = 1;
			subLR->_bf = 0;
		}
		else if (bf == 1)
		{
			subL->_bf = -1;
			parent->_bf = 0;
			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 == 0)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			subR->_bf = 1;
			subRL->_bf = 0;
		}
		else if (bf == 1)
		{
			parent->_bf = -1;
			subR->_bf = 0;
			subRL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}
	int Height(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);
		return rightHeight > leftHeight ? rightHeight + 1 : leftHeight + 1;
	}

	bool IsBalance()
	{
		return _IsBalance(_root);
	}
	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);
		if ((rightHeight - leftHeight) != root->_bf)
		{
			cout << "现在是:" << root->_bf << endl;
			cout << "应该是:" << rightHeight - leftHeight << endl;
			return false;
		}
		return abs(rightHeight - leftHeight) < 2 && _IsBalance(root->_left)&&_IsBalance(root->_right);
	}

	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 (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		//插入节点
		cur = new Node(kv);
		if (kv.first < parent->_kv.first)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

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

			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)
			{
				//当前子树出问题了,需要旋转平衡一下
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				break;
			}
			else
			{
				//理论而言不可能出现这个情况
				assert(false);
			}
		}
		return true;
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (key > cur->_kv.first)
			{
				cur = cur->_right;
			}
			else if (key < cur->_kv.first)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}
private:
	Node* _root = nullptr;
};

#include "AVLTree.h"
void TestRotateR()
{
	AVLTree<int,int> t;
	t.Insert(make_pair(5, 1));
	t.Insert(make_pair(4, 1));
	t.Insert(make_pair(3, 1));
	t.Insert(make_pair(2, 1));
	t.Insert(make_pair(1, 1));
	t.Insert(make_pair(0, 1));
	t.InOrder();
	cout << t.IsBalance() << endl;
}
void TestRotateL()
{
	AVLTree<int, int> t;
	t.Insert(make_pair(0, 1));
	t.Insert(make_pair(1, 1));
	t.Insert(make_pair(2, 1));
	t.Insert(make_pair(3, 1));
	t.Insert(make_pair(4, 1));
	t.Insert(make_pair(5, 1));
	t.InOrder();
	cout << t.IsBalance() << endl;
}
void Testbf()
{
	AVLTree<int,int> t;
	t.Insert(make_pair(5, 1));
	t.Insert(make_pair(7, 1));
	t.Insert(make_pair(3, 1));
	t.Insert(make_pair(4, 1));
	t.Insert(make_pair(2, 1));
	t.Insert(make_pair(8, 1));
	t.Insert(make_pair(9, 1));
	t.Insert(make_pair(6, 1));
	t.Insert(make_pair(1, 1));
	t.Insert(make_pair(11, 1));
	t.InOrder();
	cout << t.IsBalance() << endl;
}
void TestRL()
{
	AVLTree<int,int> t;
	t.Insert(make_pair(60, 1));
	t.Insert(make_pair(50, 1));
	t.Insert(make_pair(90, 1));
	t.Insert(make_pair(100, 1));
	t.Insert(make_pair(80, 1));
	t.Insert(make_pair(70, 1));
	t.InOrder();
	cout << t.IsBalance() << endl;
}
void TestLR()
{
	AVLTree<int, int> t;
	t.Insert(make_pair(90, 1));
	t.Insert(make_pair(100, 1));
	t.Insert(make_pair(60, 1));
	t.Insert(make_pair(50, 1));
	t.Insert(make_pair(70, 1));
	t.Insert(make_pair(80, 1));
	t.InOrder();
	cout << t.IsBalance() << endl;
}
int main()
{
	TestRotateR();
	TestRotateL();
	Testbf();
	TestRL();
	TestLR();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值