数据结构——AVL树

搜索二叉树能够在二叉树情况比较好的情况下,使查找的时间复杂度达到O(logN)。
但是,它的查找的时间复杂度依旧是O(N),面临的情况是所有的树都只有左/右子树的情况下。
那么今天介绍的AVL树就是解决这一情况的。
但是由于AVL树对我来说有些复杂,所以只讨论插入节点。这其实也有了查和改,没有删除。

文章目录

  • 1.AVL树
    • 1). 初步认识AVL树
    • 2). AVL树的节点
    • 3). AVL树的节点的插入
    • 4). AVL树的旋转
      • a. 左单旋
      • b. 情况举例
      • c. 右单旋
      • d. 右左双旋
      • e. 左右双旋
    • 5). 检验与总结

1.AVL树

1). 初步认识AVL树

AVL树是在搜索树的基础上对搜索树提出了额外的规则和调整的方法的一种树,它提出了 平衡因子的概念,利用平衡因子来体现搜索树是否平衡,而当平衡因子是非常规值的时候就会通过旋转树来让平衡因子重新达到平衡。
而平衡因子就是一棵树的右子树的高度减去左子树的高度,这个值的绝对值不大于 1,则认为这棵树是平衡的举个例子:
数据结构——AVL树_第1张图片

像这棵树就是平衡二叉树,
数据结构——AVL树_第2张图片

而这棵树不符合上述我们对平衡二叉树的要求,所以不是平衡二叉树,得需要旋转调整:
数据结构——AVL树_第3张图片
现在,它就是一颗平衡二叉树了。所以接下来来让我们实现这一数据结构:

2). AVL树的节点

与二叉搜索树不同,平衡二叉树中多了两个成员,一个是平衡因子,一个是指向父亲节点的指针:
而这里我少做了些调整,将存储数据换为pair结构体的Key-Value形。

template<class Key, class Val>
struct AVLTreeNode
{
	AVLTreeNode<Key, Val>* _left;
	AVLTreeNode<Key, Val>* _right;
	AVLTreeNode<Key, Val>* _parent;
	pair<Key, Val> _kv;

	int _bf;//balance factor

	AVLTreeNode(const pair<Key, Val>& pa)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(pa)
		,_bf(0)
	{}

};

3). AVL树的节点的插入

AVL树前面说过,它是在二叉搜索树的基础上增加一些规则形成的,所以前半部分与二叉搜索树的插入一样:

bool Insert(const pair<Key, Val>& pa)
{
	if (_root == nullptr)
	{
		_root = new Node(pa);
		size++;
		return true;
	}
	else
	{
		//正常按搜索树规则插入节点
		Node* cur = _root;
		Node* parent = _root;
		while (cur)
		{
			parent = cur;
			if (cur->_kv.first > pa.first)
				cur = cur->_left;
			else if (cur->_kv.first < pa.first)
				cur = cur->_right;
			else
				return false;
		}

		cur = new Node(pa);

		if (parent->_kv.first < cur->_kv.first) 
			parent->_right = cur;
		else if(parent->_kv.first > cur->_kv.first) 
			parent->_left = cur;

		//更新插入节点的父亲节点
		cur->_parent = parent;


		//开始遵守平衡二叉树的规则。。。
		
		return true;
	}
}

当插入节点后,再开始更新平衡因子,然后根据平衡因子进行调整。我们可以从中找出一些规律后开始书写代码:以这棵树为例:
数据结构——AVL树_第4张图片

我们先插入一个数字为33的节点会得到这样的情况:

数据结构——AVL树_第5张图片
我们插入一个数字为25的节点会得到这样的情况:

数据结构——AVL树_第6张图片
我们插入一个数字为55的节点会得到这样的情况:
数据结构——AVL树_第7张图片
我们发现我们插入的新节点的平衡因子总是0,而且新增节点只会影响祖先节点,不会影响旁支
我们还发现,当向上更新祖先平衡因子时并不是只更新父亲,也不是一直更新到根节点,那这有没有规律呢?其实是有的:

当父亲节点的平衡因子更新后变成0的时候,说明它是从-1或者1变来的,也说明了它增加这个节点之后
并没有增加父亲这颗子树的高度,所以更新了父亲节点的平衡因子就不需要向上更新了。

而当父亲节点的平衡因子更新后变成-1/1的时候,说明原来父亲节点的平衡因子是0,处于一种平衡的状态,
而后又打破了平衡,增加了父亲子树的高度,所以需要更新父亲节点的平衡因子后继续向上更新。

而且新增节点是父亲节点的左子树的时候父亲的平衡因子++,右子树时--

依据上述我们可以写出部分代码:

			while (parent)
			{
				//调整平衡因子
				//如果插入节点在父亲节点的左边,减减
				//右边加加
				if (parent->_left == cur) parent->_bf--;
				else if(parent->_right == cur) parent->_bf++;

				//如果调整平衡因子后父亲节点的平衡因子为0,说明以前的高度是1或者-1,高度没发生变化不用向上调整
				//如果为1或者-1需要向上调整平衡因子
				if (parent->_bf == 0) break;
				else if (parent->_bf == -1 || parent->_bf == 1) parent = parent->_parent, cur = cur->_parent;
				else 
				{
					
				}
			}

这是我们碰到插入节点后平衡因子没有出现违反规则的情况,那假如我们插入一个65的节点呢?
数据结构——AVL树_第8张图片
我们发现它的平衡因子就不符合规则了,那这种情况下,就需要旋转来调整。

4). AVL树的旋转

由于二叉树所种多样,不可能将所有的情况都一一列举,所以我们使用一种统一的视角来看待AVL树的旋转:
我们在上面碰到的两种不平衡的情况都是需要左单旋来解决:

a. 左单旋

数据结构——AVL树_第9张图片

最后一个树中,a是一颗高度为h的子树,b、c是高度为h-1的子树,然后在c子树中增加新节点。导致根节点(暂且是根节点)的平衡因子变成了2。需要旋转:
在这里我们对一些节点起一些名字:
数据结构——AVL树_第10张图片

这时候需要我们对parent进行一次左旋,旋转的具体做法就是将subR的左子树subLR给parent,
变成parent的右子树,然后把parent变为subR的左子树:
数据结构——AVL树_第11张图片
此时我们可以看到,parent的平衡因子变成了0,subR的变成了0。
接下来代码实现:

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

	parent->_right = subRL;
	parent->_parent = subR;


	if(subRL)//这里处理c就是新增的情况,防止subL为空
		subRL->_parent = parent;

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

	if (graparent)
	{
		if (graparent->_left == parent) graparent->_left = subR;
		else graparent->_right = subR;
	}
	else
		_root = subR;

	parent->_bf = subR->_bf = 0;
}
而左单旋的特征就是:parent的平衡因子是2,subR为1

b. 情况举例

为什么不一一列举,假如上面的h-1等于2的话,说明abc都是一个高度为2的子树那么首先高度为2的子树就有3种情况,而在c树上新增一个节点可能有8种情况,组合一下就是216种情况,列举不完…
数据结构——AVL树_第12张图片

c. 右单旋

右单旋和左单旋对称,不多说,代码如下:

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

	parent->_left = subLR;
	parent->_parent = subL;


	if (subLR)
		subLR->_parent = parent;

	subL->_right = parent;
	subL->_parent = graparent;

	if (graparent)
	{
		if (graparent->_left == parent) graparent->_left = subL;
		else graparent->_right = subL;
	}
	else
		_root = subL;

	parent->_bf = subL->_bf = 0;
}
而右单旋的特征就是:parent的平衡因子是-2,subR为-1

d. 右左双旋

我们插入节点的时候可能会有这种情况,例如我们插入55:
数据结构——AVL树_第13张图片
这次我们再用一次左单旋:
数据结构——AVL树_第14张图片
没有什么变化,所以我们得使用双旋:
这时候我们需要再使用统一的图来说明:
数据结构——AVL树_第15张图片
再起名字:
数据结构——AVL树_第16张图片
这时候双旋需要我们将subR右旋:
数据结构——AVL树_第17张图片

然后再左旋parent:
数据结构——AVL树_第18张图片

如果有细心的人发现,这其实就是把b子树做了parent的右子树,c子树做了subR,然后subRL做了parent和subR的父亲,这其中新增节点的位置影响着parent和subR的平衡因子,调整方案如下:

当新增节点在b上,也就是subRL的平衡因子为-1时,parent的平衡因子为0,subRL的为0, subR的为1
当新增节点在c上,也就是subRL的平衡因子为1时,parent的平衡因子为-1,subRL的为0, subR的为0
当然还有subRL本身就是新增的情况,subRL的平衡因子为0,此时parent、subRsubRL的平衡因子都是0

代码如下:

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

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

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

e. 左右双旋

数据结构——AVL树_第19张图片
与右左双旋对称不多赘述,代码如下:

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

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

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

5). 检验与总结

上面就是AVL树的最重要的点,我们在创建好一颗平衡二叉树的时候也应该检查,它是否真的平衡。而平衡二叉树的的规则就是高度的限制,所以我们只需要看每棵树的左右子树的高度差即可,代码如下:

	bool IsBlance()
	{
		return _IsBlance(_root);
	}
	
	int Height(Node* root)
	{
		if (root == nullptr)
			return 0;

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

		return left > right ? 1 + Height(root->_left) : 1 + Height(root->_right);
	}

	int _IsBlance(Node* root)
	{
		if (root == nullptr)
			return true;
		int left = Height(root->_left);
		int right = Height(root->_right);

		return abs(right - left) > 1 ? false : _IsBlance(root->_left)
			&& _IsBlance(root->_right);
	}

最后附上整合好的AVL树:

template<class Key, class Val>
struct AVLTreeNode
{
	AVLTreeNode<Key, Val>* _left;
	AVLTreeNode<Key, Val>* _right;
	AVLTreeNode<Key, Val>* _parent;
	pair<Key, Val> _kv;

	int _bf;

	AVLTreeNode(const pair<Key, Val>& pa)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(pa)
		,_bf(0)
	{}

};


template<class Key, class Val>
class AVLTRree
{
	typedef AVLTreeNode<Key, Val> Node;
public:
	int size = 0;

	bool Insert(const pair<Key, Val>& pa)
	{
		if (_root == nullptr)
		{
			_root = new Node(pa);
			size++;
			return true;
		}
		else
		{
			Node* cur = _root;
			Node* parent = _root;
			while (cur)
			{
				parent = cur;
				if (cur->_kv.first > pa.first)
					cur = cur->_left;
				else if (cur->_kv.first < pa.first)
					cur = cur->_right;
				else
					return false;
			}

			cur = new Node(pa);

			if (parent->_kv.first < cur->_kv.first) 
				parent->_right = cur;
			else if(parent->_kv.first > cur->_kv.first) 
				parent->_left = cur;

			cur->_parent = parent;

			while (parent)
			{
				if (parent->_left == cur) parent->_bf--;
				else if(parent->_right == cur) parent->_bf++;

				if (parent->_bf == 0) break;
				else if (parent->_bf == -1 || parent->_bf == 1) parent = parent->_parent, cur = cur->_parent;
				else if (parent->_bf == 2 || parent->_bf == -2)
				{
					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);
				}
				else assert(false);
			}
			size++;
			return true;
		}
	}

	bool IsBlance()
	{
		return _IsBlance(_root);
	}

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

private:

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

		_InOrder(root->_left);
		//cout << root->_kv.first << " ";
		printf("%2d ", root->_kv.first);
		_InOrder(root->_right);
	}

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

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

		return left > right ? 1 + Height(root->_left) : 1 + Height(root->_right);
	}

	int _IsBlance(Node* root)
	{
		if (root == nullptr)
			return true;
		int left = Height(root->_left);
		int right = Height(root->_right);

		return abs(right - left) > 1 ? false : _IsBlance(root->_left)
			&& _IsBlance(root->_right);
	}

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

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

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

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

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

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

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

		parent->_right = subRL;
		parent->_parent = subR;


		if(subRL)
			subRL->_parent = parent;

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

		if (graparent)
		{
			if (graparent->_left == parent) graparent->_left = subR;
			else graparent->_right = subR;
		}
		else
			_root = subR;

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

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

		parent->_left = subLR;
		parent->_parent = subL;


		if (subLR)
			subLR->_parent = parent;

		subL->_right = parent;
		subL->_parent = graparent;

		if (graparent)
		{
			if (graparent->_left == parent) graparent->_left = subL;
			else graparent->_right = subL;
		}
		else
			_root = subL;

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

	Node* _root = nullptr;
};

你可能感兴趣的:(数据结构,c++)