详解set/map的底层结构——AVL树和红黑树

目录

前文

一,AVL树

1.1 什么是AVL树?

1.2 AVL树节点的定义

1.3 insert—插入(重点)

 1.4 旋转(重点)

1.4.1 右单旋

 1.4.2 左单旋

 1.4.3 左右双旋

 1.4.4 右左双旋

 1.5 IsBalanc(平衡判断)

1.6 中序遍历

1.7 测试

二,红黑树

2.1 什么是红黑树?

2.2 红黑树的性质

2.3 红黑树节点的定义

2.4 插入

2.5 插入调整

2.5.1 情况一

2.5.2 情况二

2.5.3 情况三

2.6 IsBalance平衡判断

2.7 中序遍历以及求高度

2.7.1 中序遍历

2.7.2 高度

2.8 测试

三,AVL树和红黑树源码

3.1 AVL树

3.2 红黑树

总结



前文

本文带领大家深入了解set/map的底层结构——AVL树和红黑树,如果老铁对set和map不太熟悉,推荐先学习一下set和map的基本介绍和基本用法。

一,AVL树

1.1 什么是AVL树?

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度

AVL树的规则如下

1.它的左右子树都是AVL树

2.左右子树高度之差(这里我们简称为平衡因子)的绝对值不超过1.

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

1.2 AVL树节点的定义

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)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _bf(0)
	{}
};

节点我们采用三叉链的形式和之前的搜索二叉树类似,但是多了一个平衡因子_bf

(平衡因子=右子树高度-左子树高度)

1.3 insert—插入(重点)

首先这里插入的基本逻辑和搜索二叉树类似

1.树为空,则直接new一个新节点给root

2.树不为空,通过二叉树查找的规则找到空位置,插入即可

 

但是这里要多出一个步骤,同时也是重中之重

那就是调整平衡因子

更新平衡因子的规则如下:

1.cur在父节点的右边,父节点的平衡因子++

2.cur在父节点的左边,父节点的平衡因子--

然后因为平衡因子的变化我们还需要向上更新平衡因子,更新的规则如下

a.  bf == -1 || bf == 1:说明该节点的平衡因子变成了1/-1,因此该节点的平衡因子原来一定是0,此时需要向上更新

b. bf == -2 || bf == 2:说明该节点的左右子树已经打破平衡,需要进行旋转调整

c. bf == 0:说明该节点经过插入后高度正好相等,无需在向上更新

 1.4 旋转(重点)

AVL树的精髓便是通过旋转保持左右子树的平衡,从而防止歪脖子树的高度,在上面的插入中,更新平衡因子后,情况b. bf == -2 || bf == 2需要进行旋转从而保持左右子树的平衡。首先先让我们了解一下旋转的原因以及目的

旋转的原因:平衡因子大于1或者小于-1时,左右子树不平衡

旋转的目的:降低树的高度,保持左右子树的平衡,同时保持搜索二叉树的性质

旋转主要分为以下四种情况

 接下来我们就逐个讲解以下各个旋转的发生条件以及旋转具体实现

1.4.1 右单旋

 如图所示,当新节点要插入较高左子树的左侧时就会导致parent的左右失衡,此时就需要进行右单旋调整

首先我们根据二叉搜索树的性质得知:parent>subL,subL<subLR<parent

旋转步骤如下:

1.parent成为subL的右

2.subLR成为parent的左

经过上述旋转既降低了树的高度,维持左右子树的平衡,又保持了二叉搜索树的性质

代码实现如下(有更详细的注释):

 1.4.2 左单旋

 如图所示,当新节点要插入较高右子树右侧时,也就是在c的位置插入新节点,就会导致parent的左右子树失衡,这时候就需要进行左单旋调整

首先我们根据搜索二叉树的性质的得知:parent<subR,parent<subRL<subR

旋转步骤:

1.parent成为subR的左树

2.subRL成为parent的右子树

经过上述旋转既降低了树的高度,维持左右子树的平衡,又保持了二叉搜索树的性质

代码如下(由于左右类似,这里我们不在进行详细注释):

 1.4.3 左右双旋

 如上图所示,当新节点要插入较高子树的右侧时,也就是在b或者c插入,就会导致parent的左右子树失衡,此时需要左右双旋

 具体步骤如下:

1.以subL为parent进行左单旋

2.以parent为parent进行右单旋

3.调整平衡因子

在调整平衡因子这一步需要注意:

a. 如果新节点插入后,subLR的平衡因子为-1,那么最后parent的平衡因子为1。

b. 如果新节点插入后,subLR的平衡因子为1,那么最后subL的平衡因子为-1.

c. 如果新节点插入后,subLR的平衡因子为0,那么无需进行特殊调整

代码如下:

 1.4.4 右左双旋

  如上图所示,当新节点要插入较高子树的左侧时,也就是在b或者c插入,就会导致parent的左右子树失衡,此时需要右左双旋

 具体步骤如下:

1.以subR为parent进行右单旋

2.以parent为parent进行左单旋

3.调整平衡因子

在调整平衡因子这一步需要注意:

a. 如果新节点插入后,subRL的平衡因子为-1,那么最后subR的平衡因子为1。

b. 如果新节点插入后,subRL的平衡因子为1,那么最后parent的平衡因子为-1.

c. 如果新节点插入后,subRL的平衡因子为0,那么无需进行特殊调整

代码如下:

 1.5 IsBalanc(平衡判断)

根据AVL树的性质,我们只需要判断左右子树的高度差是否大于1即可,这里我们走的是一个中序遍历

    //检查树的高度是否平衡
	bool IsBalance()
	{
		return _IsBalance(_root);
	}

	//树的高度
	int TreeHeight()
	{
		return _TreeHeight(_root);
	}
    //树的高度
	int _TreeHeight(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}

		int Lheight = _TreeHeight(root->_left);
		int Rheight = _TreeHeight(root->_right);

		return Lheight > Rheight ? Lheight + 1 : Rheight + 1;
	}
	//检查树的高度是否平衡
	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		int Lheight = _TreeHeight(root->_left);
		int Rheight = _TreeHeight(root->_right);

		return abs(Rheight - Lheight) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);

	}

1.6 中序遍历

//中序遍历
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
//中序遍历_递归
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

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

1.7 测试

大致框架完成后,我们来简单测试一下

void Test_AVLTree1()
{
	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	AVLTree<int, int> t1;
	for (auto e : a)
	{
		/*	if (e == 14)
			{
			int x = 0;
			}*/

		t1.Insert(make_pair(e, e));
		cout << e << "插入:" << t1.IsBalance() << endl;
	}

	t1.InOrder();
	cout << t1.IsBalance() << endl;
}

// 10:35继续
void Test_AVLTree2()
{
	srand(time(0));
	const size_t N = 500000;
	AVLTree<int, int> t;
	for (size_t i = 0; i < N; ++i)
	{
		size_t x = rand() + i;
		t.Insert(make_pair(x, x));
		//cout << t.IsBalance() << endl;
	}

	//t.Inorder();

	cout << t.IsBalance() << endl;
	cout << t.TreeHeight() << endl;
}

 欧克,测试通过

二,红黑树

讲完AVL树,我们就要讲一下绝对经典的红黑树了

2.1 什么是红黑树?

红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。 红黑树是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)。后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的“红黑树”。 红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。 它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。 

具体来说,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的

2.2 红黑树的性质

1. 每个结点不是红色就是黑色
2. 根节点是黑色的 
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的(重点)
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点 (重点)
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

那么红黑树是如何根据上面的性质来保证,最长路径不超过最短路径的2倍

 如上图是一个简单的没有红色节点的红黑树,每条路径的黑色节点数量相同,假设我们在最左边路径中添加红色节点,但是因为规则三,所以红色节点不能连续添加,所以最多只能加三个,也就说,在保证是红黑树的条件下,最短路径最长只能达到原路径的二倍,因此就保证了在节点树为N的情况下,红黑树中的所有路径长度都处于[logN,2logN]的范围内,从而达到接近平衡

2.3 红黑树节点的定义

enum Colour
{
	RED,
	BLACK,
};

template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	pair<K, V> _kv;
	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _col(RED)
	{}
};

红黑树的节点和AVL树相比,少了一个平衡因子,多了一个枚举类型Colour来表示颜色,注意这里的新节点插入,默认插入红色,这样避免破坏规则四,方便调整

2.4 插入

首先这里插入的基本逻辑和搜索二叉树类似

1.树为空,则直接new一个新节点给root

2.树不为空,通过二叉树查找的规则找到空位置,插入即可

3.根据情况进行调整

 

这里插完之后需要进行调整,调整的情况如下:

1.如果插入节点的父节点是黑色,则没有破坏规则,拍拍屁股走人既可

2.如果插入节点的父节点是红色,则破坏了规则,需要进行变色或者旋转调整

2.5 插入调整

注意:cur为新插入节点,cur的父节点叫做parent,cur的祖父节点叫做grandfather,另外还需要标注出uncle

调整大概分为三种情况(下面的情况都是以cur插入到grandfather的左树为例子,右树反过来即可):

情况一:cur为红,parent为红,uncle为红

情况二:cur为红,parent为红,uncle为黑/uncle不存在,cur是parent的左孩子

情况三:cur为红,parent为红,uncle为黑/uncle不存在,cur是parent的右孩子

2.5.1 情况一

处理方式:进行变色即可,将p和u变黑,g变红,注意这里的树可能是子树,也可能是个完整的树,如果是个完整的树,再将g变黑,如果不是则继续向上调整,因为g变红可能会对上面造成影响

2.5.2 情况二

 处理方式:旋转+变色,先以g为parent节点进行右旋(如果是右树进行左旋),然后p变黑,g变红即可。

2.5.3 情况三

处理方式:双旋转+变色

1.cur属于g的左子树:则先以parent为根节点进行左旋,这样就转换成了情况二,再以g为根节点进行右旋,最后进行变色,cur变黑,g变红

2.cur属于g的右子树:则是先右旋再左旋,最后变色,多的不在赘述,细节和上面类似

调整的所有代码如下:

//调整

		while (parent && parent->_col == RED)
		{
			//cur在g的左子树上
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				//情况一,cur为红,p为红,u存在且为红
				if (uncle && uncle->_col == RED)
				{
					//更新颜色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;
					if (grandfather == _root)//如果是整树,需要将g的颜色变为黑
					{
						grandfather->_col = BLACK;
					}
					else//如果是子树则继续向上调整
					{
						cur = grandfather;
						parent = cur->_parent;
					}
				}
				//情况二,情况三可以归为一大类:cur为红,p为红,u为黑或者不存在
				else
				{
					//情况二,cur为p的左子树,右单旋+变色
					if (cur == parent->_left)
					{
						_RotateR(grandfather);//右单旋
						//变色
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					//情况三,cur为p的右子树,左单旋+右单旋+变色
					else
					{
						_RotateL(parent);//左单旋
						_RotateR(grandfather);//右单旋
						//变色
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;//当调整到这里时,无需继续向上调整,因为该子树的祖节点为黑色
				}
			}
			//cur在g的右子树
			else
			{
				Node* uncle = grandfather->_left;
				//情况一,cur为红,p为红,u存在且为红
				if (uncle && uncle->_col == RED)
				{
					//更新颜色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;
					if (grandfather == _root)//如果是整树,需要将g的颜色变为黑
					{
						grandfather->_col = BLACK;
					}
					else//如果是子树则继续向上调整
					{
						cur = grandfather;
						parent = cur->_parent;
					}
				}
				//情况二,情况三可以归为一大类:cur为红,p为红,u为黑或者不存在
				else
				{
					//情况二,cur为p的右子树,左单旋+变色
					if (cur == parent->_right)
					{
						_RotateL(grandfather);//左单旋
						//变色
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					//情况三,cur为p的左子树,右单旋+左单旋+变色
					else
					{
						_RotateR(parent);//右单旋
						_RotateL(grandfather);//左单旋
						//变色
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;//当调整到这里时,无需继续向上调整,因为该子树的祖节点为黑色
				}
			}
		}

2.6 IsBalance平衡判断

红黑树的平衡判断我们也是根据规则来:

1. 每个结点不是红色就是黑色
2. 根节点是黑色的 
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的(重点)
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点 (重点)
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

 检查方法如下:一一对应

1.无需检查

2.检查根节点是否为黑色即可

3.规则三意思是不能有连续红色,检查每个红色节点的parent,如果parent也为红色,则违反规则

4.利用传值传参,记录该节点的路径上的黑色节点数目,然后和第一条路径的黑色节点数目作比较,不相等则违反规则

5.无需检查

 代码如下

//检查是否平衡/是否符合红黑树规则
	bool IsBalance()
	{
		int FPathNumber = -1;
		return _check(_root,0,FPathNumber);
		
	}
    //检查
	bool _check(Node* root,int BlackNumber,int& FPathNumber)
	{
		//检查规则二,检查根节点
		if (_root->_col == RED)
		{
			cout << "根节点不为黑,违反规则二" << endl;
			return false;
		}

		if (root == nullptr)
		{
			//检查路径黑色节点是否相等
			if (FPathNumber == -1)
			{
				FPathNumber = BlackNumber;
			}

			if (FPathNumber != BlackNumber)
			{
				cout << "路径的黑色节点数量不同,违反规则四" << endl;
				return false;
			}
			return true;
		}

2.7 中序遍历以及求高度

2.7.1 中序遍历

    //中序遍历
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
    //中序遍历_递归
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

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

2.7.2 高度

    //树的高度
	int TreeHeight()
	{
		return _TreeHeight(_root);
	}
    //树的高度
	int _TreeHeight(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}

		int Lheight = _TreeHeight(root->_left);
		int Rheight = _TreeHeight(root->_right);

		return Lheight > Rheight ? Lheight + 1 : Rheight + 1;
	}

2.8 测试

测试代码如下

void Test_RBTree1()
{
	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	RBTree<int, int> t1;
	for (auto e : a)
	{
		t1.Insert(make_pair(e, e));
		//cout << e << "插入:" << t1.IsBalance() << endl;
	}

	t1.InOrder();
	cout << t1.IsBalance() << endl;
}

void Test_RBTree2()
{
	srand(time(0));
	const size_t N = 5000000;
	RBTree<int, int> t;
	for (size_t i = 0; i < N; ++i)
	{
		size_t x = rand() + i;
		t.Insert(make_pair(x, x));
		//cout << t.IsBalance() << endl;
	}

	//t.Inorder();

	cout << t.IsBalance() << endl;
	cout << t.TreeHeight() << endl;
}

 测试完成无误

三,AVL树和红黑树源码

3.1 AVL树

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>
#include <time.h>
using namespace std;
//avl树的实现
//结点
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)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _bf(0)
	{}
};

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	AVLTree()
		:_root(nullptr)
	{}

	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->_right == cur)
			{
				parent->_bf++;
			}
			else
			{
				parent->_bf--;
			}

			//因子为1或-1,继续向上调整
			if (parent->_bf == -1 || parent->_bf == 1)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			//因子为-2或者2,说明左右子树已经打破平衡,需要进行调整
			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)
				{
					_RotateLR(parent);
				}
				//右左双旋
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					_RotateRL(parent);
				}
				else
				{
					assert(false);
				}
				break;
			}
			//因子为0,则左右子树插入后正好平衡,无需调整
			else if (parent->_bf == 0)
			{
				break;
			}
			//其他值,插入错误
			else
			{
				assert(false);
			}
		}

		return true;


	}
	//中序遍历
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	//检查树的高度是否平衡
	bool IsBalance()
	{
		return _IsBalance(_root);
	}

	//树的高度
	int TreeHeight()
	{
		return _TreeHeight(_root);
	}
private:
	//树的高度
	int _TreeHeight(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}

		int Lheight = _TreeHeight(root->_left);
		int Rheight = _TreeHeight(root->_right);

		return Lheight > Rheight ? Lheight + 1 : Rheight + 1;
	}
	//检查树的高度是否平衡
	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		int Lheight = _TreeHeight(root->_left);
		int Rheight = _TreeHeight(root->_right);

		return abs(Rheight - Lheight) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);

	}



	//中序遍历_递归
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

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

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

		Node* ppnode = parent->_parent;

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

		parent->_left = subLR;
		//判断subLR不为空
		if (subLR)
			subLR->_parent = parent;

		//判断parent是否为根节点
		if (parent==_root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			subL->_parent = ppnode;
			if (ppnode->_right == parent)
			{
				ppnode->_right = subL;
			}
			else
			{
				ppnode->_left = subL;
			}
		}
		parent->_bf = subL->_bf = 0;
	}

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

		Node* ppnode = parent->_parent;//记录parent的父节点后续根据ppnode调整subR的父节点

		parent->_right = subRL;
		if (subRL)//判断subRL是否不为空,为空则不能非法访问
			subRL->_parent = parent;

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

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

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

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

		//记录subLR的平衡因子后续需要根据这个平衡因子调整subL parent的平衡因子
		int bf = subLR->_bf;
		//左旋
		_RotateL(parent->_left);
		//右旋
		_RotateR(parent);

		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 if (bf == 0)
		{
			subL->_bf = 0;
			parent->_bf = 0;
			subLR->_bf = 0;
		}
		else
		{
			assert(subLR);
		}
	}

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

		int bf = subRL->_bf;

		//右旋
		_RotateR(parent->_right);
		//左旋
		_RotateL(parent);

		//调整平衡因子
		if (bf == 1)
		{
			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(bf);
		}
	}

private:
	Node* _root;
};

3.2 红黑树

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>
#include <time.h>
using namespace std;

//节点

enum Colour
{
	RED,
	BLACK,
};

template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	pair<K, V> _kv;
	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _col(RED)
	{}
};

template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	RBTree()
		:_root(nullptr)
	{}

	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}

	//插入
	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			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 && parent->_col == RED)
		{
			//cur在g的左子树上
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				//情况一,cur为红,p为红,u存在且为红
				if (uncle && uncle->_col == RED)
				{
					//更新颜色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;
					if (grandfather == _root)//如果是整树,需要将g的颜色变为黑
					{
						grandfather->_col = BLACK;
					}
					else//如果是子树则继续向上调整
					{
						cur = grandfather;
						parent = cur->_parent;
					}
				}
				//情况二,情况三可以归为一大类:cur为红,p为红,u为黑或者不存在
				else
				{
					//情况二,cur为p的左子树,右单旋+变色
					if (cur == parent->_left)
					{
						_RotateR(grandfather);//右单旋
						//变色
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					//情况三,cur为p的右子树,左单旋+右单旋+变色
					else
					{
						_RotateL(parent);//左单旋
						_RotateR(grandfather);//右单旋
						//变色
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;//当调整到这里时,无需继续向上调整,因为该子树的祖节点为黑色
				}
			}
			//cur在g的右子树
			else
			{
				Node* uncle = grandfather->_left;
				//情况一,cur为红,p为红,u存在且为红
				if (uncle && uncle->_col == RED)
				{
					//更新颜色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;
					if (grandfather == _root)//如果是整树,需要将g的颜色变为黑
					{
						grandfather->_col = BLACK;
					}
					else//如果是子树则继续向上调整
					{
						cur = grandfather;
						parent = cur->_parent;
					}
				}
				//情况二,情况三可以归为一大类:cur为红,p为红,u为黑或者不存在
				else
				{
					//情况二,cur为p的右子树,左单旋+变色
					if (cur == parent->_right)
					{
						_RotateL(grandfather);//左单旋
						//变色
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					//情况三,cur为p的左子树,右单旋+左单旋+变色
					else
					{
						_RotateR(parent);//右单旋
						_RotateL(grandfather);//左单旋
						//变色
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;//当调整到这里时,无需继续向上调整,因为该子树的祖节点为黑色
				}
			}
		}
		return true;
	}

	//中序遍历
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	//检查是否平衡/是否符合红黑树规则
	bool IsBalance()
	{
		//检查根节点
		int FPathNumber = -1;
		return _check(_root,0,FPathNumber);	
	}

	//树的高度
	int TreeHeight()
	{
		return _TreeHeight(_root);
	}
private:
	//析构函数
	void _Destroy(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}

	//树的高度
	int _TreeHeight(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}

		int Lheight = _TreeHeight(root->_left);
		int Rheight = _TreeHeight(root->_right);

		return Lheight > Rheight ? Lheight + 1 : Rheight + 1;
	}

	//检查
	bool _check(Node* root,int BlackNumber,int& FPathNumber)
	{
		//检查规则二,检查根节点
		if (_root->_col == RED)
		{
			cout << "根节点不为黑,违反规则二" << endl;
			return false;
		}

		if (root == nullptr)
		{
			//检查路径黑色节点是否相等
			if (FPathNumber == -1)
			{
				FPathNumber = BlackNumber;
			}

			if (FPathNumber != BlackNumber)
			{
				cout << "路径的黑色节点数量不同,违反规则四" << endl;
				return false;
			}
			return true;
		}

		//规则四:求每条路径黑色节点数量,如果root为黑色节点,BlackNumber++
		if (root->_col == BLACK)
		{
			BlackNumber++;
		}

		//判断规则三,是否有连续红色节点
		Node* parent = root->_parent;
		if (root->_col == RED && parent && parent->_col == RED)
		{
			cout << "出现连续红色节点,违反规则三" << endl;
			return false;
		}

		return _check(root->_left,BlackNumber,FPathNumber)
			&& _check(root->_right, BlackNumber, FPathNumber);
	}

	//中序遍历_递归
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

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





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

		Node* ppnode = parent->_parent;//记录parent的父节点后续根据ppnode调整subR的父节点

		parent->_right = subRL;
		if (subRL)//判断subRL是否不为空,为空则不能非法访问
			subRL->_parent = parent;

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

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

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

		Node* ppnode = parent->_parent;

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

		parent->_left = subLR;
		//判断subLR不为空
		if (subLR)
			subLR->_parent = parent;

		//判断parent是否为根节点
		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			subL->_parent = ppnode;
			if (ppnode->_right == parent)
			{
				ppnode->_right = subL;
			}
			else
			{
				ppnode->_left = subL;
			}
		}
	}
private:
	Node* _root;
};

总结

这篇文章主要是带领大家深入了解了一下map和set的底层数据结构AVL树以及红黑树

AVL树的精髓主要是平衡因子,以及通过旋转来降低树的高度,使树变得平衡。

而红黑树的精髓则是在大多数情况下可以通过变色来减少旋转的次数,借此提高效率。

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值