STL--AVLTree、RBTree介绍和插入的模拟实现

本文深入探讨了平衡二叉搜索树的概念,以AVL树为例详细解释了其节点定义、插入操作及旋转调整过程。同时,介绍了红黑树的特性,包括插入操作后的颜色调整和旋转策略,以保持树的平衡。通过对比,指出红黑树在频繁插入操作中较AVL树更具优势。
摘要由CSDN通过智能技术生成

一、平衡二叉搜索树

对于map/multimap/set/multiset 这几个容器的共同点是:其底层都是按照二叉搜索树来实现的。但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉树搜索树就会退化成单支树,时间复杂度会退化成O(N),因此map/set等关联式容器的底层结构是对二叉树进行了平衡,即采用二叉平衡搜索树来实现的。

二、AVLTree

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

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

1、它的左右子树是空树。

2、右子树减去左子树的高度(简称平衡因子) 的绝对值不超过1(-1/0/1)。

在这里插入图片描述

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

1、AVL树节点的定义

在这里插入图片描述

template<class K,class V>
struct AVLTreeNode{
	AVLTreeNode<K,V>* parent;	//该节点的父节点
	AVLTreeNode<K,V>* left;	//该节点的左孩子
	AVLTreeNode<K,V>* right;	//该节点的右孩子
	std::pair<K,V> kv;					
	int bf;					//该节点的平衡因子
	
	AVLTreeNode(const std::pair<K,V>& _kv)
		:parent(nullptr),left(nullptr),right(nullptr),kv(_kv),bf(0)
	{}
	
};

2、AVLTree的插入

AVLTree就是在二叉搜索树的基础上引入了平衡因子,因此AVLTree也可以看成是二叉搜索树。那么AVLTree的插入过程可以分两步:

1、按照二叉搜索树的方式插入新节点。

在这里插入图片描述

2、调整节点的平衡因子。

在这里插入图片描述

C++代码:
template<class K, class V>
struct AVLTree {
	typedef AVLTreeNode<K, V> Node;
public:

	pair<Node*,bool> insert(const pair<K, V>& kv){
		if(root==nullptr){	//当root节点不存在时,创建节点
			root=new Node(kv);
			return make_pair(root,true);	//插入成功
		}
		Node* _parent=nullptr;
		Node* cur=root;
		//找节点应该插入的位置
		while(cur){
			if(cur->kv.first>kv.first){
				_parent=cur;
				cur=cur->left;
			}else if(cur->kv.first<kv.first){
				_parent=cur;
				cur=cur->right;
			}else{
				return make_pair(cur,false);	//节点已经存在,插入失败
			}
		}
		cur=new Node(kv);
		
		if(_parent->kv.first > kv.first){
			_parent->left=cur;
			cur->parent=_parent;
		}else{
			_parent->right=cur;
			cur->parent=_parent;
		}
		
		Node* newNode=cur;//保存插入节点,因为调节平衡因子时要改变cur指针
		
		//向上调节平衡因子
		while(_parent){
			//更新父亲节点的平衡因子
			if(_parent->left==cur){
				_parent->bf--;
			}else{
				_parent->bf++;
			}
		
			if(_parent->bf==0){//更新结束,达到平衡
				break;
			}else if(abs(_parent->bf)==1){	// 继续向上调整
				cur=_parent;
				_parent=_parent->parent;
			}else if(abs(_parent->bf)==2){	//旋转
				if(_parent->bf==2){
					if(cur->bf==1){
						RotateL(_parent);
					}else{
						RotateRL(_parent);
					}
				}else{
					if(cur->bf==-1){
						RotateR(_parent);	
					}else{
						RotateLR(_parent);
					}
				}
				break;
			}else{
				assert(false);
			}
		}
		return make_pair(newNode,true);
	}
private:
	Node *root=nullptr;
}

3、AVLTree的旋转

如果在一棵原本平衡的AVLTree中插入一个新节点,可能造成不平横,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVLTree的旋转分为4种:

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

下图在插入之前,AVLTree是平衡的,新节点插入到30的左子树(注意是:不是左孩子)中,30左子树增加了一层,导致以60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子树增加一层,即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有右子树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新节点的平衡因子即可。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

C++代码:
void RotateR(Node* _parent){
	Node* subL=_parent->left;
	Node* subLR=subL->right;
	
	_parent->left=subLR;
	if(subLR)	//当subLR存在时
		subLR->parent=_parent;
	
	subL->right=_parent;
	_parent->parent=subL;
	
	Node* parentParent=_parent->parent;
	
	if(_parent==root){	//如果父节点根节点,则更新根节点
		root=subL;
		root->parent=nullptr;
	}else{				
		if(parentParent->left==_parent){ //如果父亲节点为祖父节点的左子树
			parentParent->left=subL;
		}else{							 //如果父亲节点为祖父节点的右子树
			parentParent->right=subL;
		}
		subL->parent=parentParent;		//将子树和原来的祖父节点连接起来
	}
	subL->bf=_parent->bf=0;				//更新平衡因子
}

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

左单旋的情况和右单旋是类似的,所以这里就不具体分析了:

在这里插入图片描述

C++代码
void RotateL(Node* _parent){
	Node* subR=_parent->right;
	Node* subRL=subR->left;
	
	_parent->right=subRL;
	if(subRL)
		subRL->parent=_parent;
	
	subR->left=_parent;
	_parent->parent=subR;
	
	Node* parentParent=_parent->parent;
	if(_parent==root){
		root=subR;
		subR->parent=nullptr;
	}else{
		if(parentParent->left==_parent){
			parentParent->left=subR;
		}else{
			parentParent->right=subR;
		}
		subR->parent=parentParent;
	}
	_parent->bf=subR->bf=0;
}

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

当新节点插入较高左子树的右侧时,则需要进行双旋转,如下图,我们可以进行复用上面的左旋和右旋转,我们需要旋转后保持搜索的特点,所以我们需要先进行左旋转,再进行右旋转。我们在旋转之后只需要调整节点的平衡因子即可:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

C++代码:
void RotateLR(Node* _parent){
	Node* subL=_parent->left;	//先保存节点,为后面更新平衡因子做准备
	Node* subLR=subL->right;
	int bf=subLR->bf;			//判断是上面三种情况的哪一种

	RotateL(subL);
	RotateR(_parent);
	
	if(bf==1){
		subLR->bf=_parent->bf=0;
		subL->bf=-1;
	}else if(bf==-1){
		subLR=subL->bf=0;
		_parent->bf=1;
	}else{
		subL->bf=subLR->bf=_parent->bf=0;
	}
}

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

右左双旋的情况和左右双旋是类似的,所以这里就不具体分析了一下是旋转的图:
在这里插入图片描述

C++代码:

void RotateRL(Node* _parent){
	Node* subR=_parent->right;
	Node* subRL-subR->left;
	
	int bf=subRL->bf;

	RotateR(subR);
	RotateL(_parent);

	if(bf==1){
		_parent->bf=-1;
		subR->bf=subRL->bf=0;
	}else if(bf==-1){
		_parent->bf=subRL->bf=0;
		subR->bf=1;
	}else{
		subR->bf=subRL->bf=_parent->bf=0;
	}
}

3.5、插入节点的整体代码

std::pair<Node*, bool> insert(const std::pair<K, V>& kv) {
		if (root == nullptr) {
			root = new Node(kv);
			return make_pair(root, true);
		}
		Node* _parent = nullptr;
		Node* cur = root;
		while (cur) {
			if (cur->kv.first > kv.first) {
				_parent = cur;
				cur = cur->left;
			}
			else if (cur->kv.first < kv.first) {
				_parent = cur;
				cur = cur->right;
			}
			else {
				return make_pair(cur, false);
			}
		}
		cur = new Node(kv);
		if (_parent->kv.first > kv.first) {
			_parent->left = cur;
			cur->parent = _parent;
		}
		else {
			_parent->right = cur;
			cur->parent = _parent;
		}
		Node* newNode = cur;
		while (_parent) {
			if (cur == _parent->right) {
				_parent->bf++;
			}
			else {
				_parent->bf--;
			}
			if (_parent->bf==0) {
				break;
			}
			else if (abs(_parent->bf) == 1) {	//继续向上更新
				cur = _parent;
				_parent = _parent->parent;
			}
			else if (abs(_parent->bf) == 2) {
				//旋转
				if (_parent->bf == -2) {
					if (cur->bf == -1) {
						RotateR(_parent);
					}
					else {
						RotateLR(_parent);
					}
				}
				else {
					if (cur->bf ==1) {
						RotateL(_parent);
					}
					else {
						RotateRL(_parent);
					}
				}
				break;
			}
			else {
				assert(false);
			}
		}
		return make_pair(newNode,true);
	}

	void RotateR(Node* _parent) {
		Node* subL = _parent->left;
		Node* subLR = subL->right;

		_parent->left = subLR;
		if (subLR)	//subLR不为空节点
			subLR->parent = _parent;
		Node* parentParent = _parent->parent;
		subL->right = _parent;
		_parent->parent = subL;

		if (_parent == root) {	//走到根节点,根节点的父亲节点为空
			root = subL;
		}
		else {
			if (parentParent->left == _parent) {
				parentParent->left = subL;
			}
			else {
				parentParent->right = subL;
			}
		}
		subL->parent = parentParent;
		subL->bf = _parent->bf = 0;
	}

	void RotateL(Node* _parent) {
		Node* subR = _parent->right;
		Node* subRL = subR->left;
		_parent->right = subRL;

		if (subRL)
			subRL->parent = _parent;
		Node* parentParent = _parent->parent;
		subR->left = _parent;
		_parent->parent = subR;
	
		if (_parent == root) {
			root = subR;
		}
		else {
			if (parentParent->left == _parent) {
				parentParent->left = subR;
			}
			else {
				parentParent->right = subR;
			}
		}
		subR->parent = parentParent;
		subR->bf = _parent->bf = 0;
	}

	void RotateLR(Node* _parent) {
		Node* subL = _parent->left;
		Node* subLR = subL->right;
		int bf = subLR->bf;
		RotateL(subL);
		RotateR(_parent);

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

	void RotateRL(Node* _parent) {
		Node* subR = _parent->right;
		Node* subRL = subR->left;
		int bf = subRL->bf;
		RotateR(subR);
		RotateL(_parent);

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

4、AVLTree的验证

AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

1、验证是二叉搜索树:中序遍历是有序。

	void _InOrder(Node* root) {
		if (!root)
			return;
		_InOrder(root->left);
		cout << root->kv.first << ' ' << root->bf << endl;
		_InOrder(root->right);
	}

	void InOrder() {
		_InOrder(root);
	}

2、验证其为平衡树:每个节点子树的左右高度差绝对值不超过1。

利用后序遍历:验证每个节点子树的左右高度差绝对值不超过1。

	bool isBalance() {
		return _isBalance(root)!=-1;
	}
	
	int _isBalance(Node* root) {
		if (!root) return 0;
		int left = _isBalance(root->left);
		if (left == -1) return -1;
		int right = _isBalance(root->right);
		if (right == -1) return -1;
		if (abs(right - left) > 1) return -1;
		return max(left, right) + 1;
	}

5、AVLTree的性能

AVLTree是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值不超过1,这样可以保证查询时的高效速度,即(log2N)。但是如果要对AVLTree做一些结果的修改操作,性能非常低下。比如:插入时要维护其绝对平衡,旋转次数太多,更差的是在删除的时候有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

6、AVLTree的拷贝构造、析构函数、拷贝赋值运算符重载、[]重载

template<class K, class V>
struct AVLTree {
	typedef AVLTreeNode<K, V> Node;
public:
	AVLTree() = default;
	
	AVLTree(const AVLTree<K, V>& t) {
		deleteNode(root);
		root=copyNode(t.root);
	}

	Node* copyNode(Node* const sNode) {
		if (!sNode) return nullptr;
		Node* root = new Node(sNode->kv);
		root->bf = sNode->bf;
		root->left = copyNode(sNode->left);
		if (root->left) root->left->parent = root;
		root->right = copyNode(sNode->right);
		if (root->right) root->right->parent = root;
		return root;
	}
	
	V& operator[](const K& key) {
		return ((*(insert(make_pair(key, V())).first)).kv).second;
	}
	
	AVLTree<K, V>& operator=(AVLTree<K, V> t) {
		swap(root,t.root);
		return *this;
	}

	//使用后序遍历析构节点
	~AVLTree() {
		deleteNode(root);
	}

	void deleteNode(Node* root) {
		if (!root) return;
		deleteNode(root->left);
		deleteNode(root->right);
		delete root;
	}
};

三、红黑树

3.1红黑树的概念

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

在这里插入图片描述

3.2红黑树的性质

1、每个节点不是红色就是黑色。

2、根节点是黑色。

3、没有连续的两个节点都是红节点。

4、对于每个节点,从该节点到其所有后代叶节点(空节点)的简单路径上,均包含相同数量的黑色节点

5、 每个叶子结点都是黑色的(此处的叶子结点指的是空结点(NIL))。

3.3 问题

为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

对于性质4:从根节点到叶子节点的黑色节点都是相同的。所以最短路径为全黑。又由于性质2和性质3:根节点为黑色,没有连续的两个节点都是红节点,所以最长路径只能是一黑一红,最终红节点和黑节点相等。所以满足上面的性质,就能保证最长路径中节点个数不会超过最短路径节点个数的两倍。

3.4 红黑树节点的定义

同样我们红黑树的定义和AVLTree节点一样的,只是把平衡因子改为了颜色属性。

enum Color {
	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;
	enum Color col;
	RBTreeNode(const pair<K, V>& _kv)
		:left(nullptr), right(nullptr), parent(nullptr), kv(_kv), col(RED)
	{}
};

为什么默认节点定义为红色?

因为我们每条路径上的黑节点是相同的,如果默认增加一个节点是黑色的话,那么增加节点的路径中黑节点增加了,不能满足性质4。所以我们默认增加一个节点为红节点。

3.5 红黑树的插入操作

3.5.1 按照二叉搜索树的规则插入节点

在这里插入图片描述

template<class K, class V>
struct RBTree {
	typedef RBTreeNode<K, V> Node;
public:

	pair<Node*, bool> insert(const pair<K, V>& kv) {
		if (root == nullptr) {
			root = new Node(kv);
			root->col = BLACK;
			return make_pair(root, true);
		}
		Node* _parent = nullptr;
		Node* cur = root;
		while (cur) {
			if (cur->kv.first > kv.first) {
				_parent = cur;
				cur = cur->left;
			}
			else if (cur->kv.first < kv.first) {
				_parent = cur;
				cur = cur->right;
			}
			else {
				return make_pair(cur, false);
			}
		}
		cur = new Node(kv);
		if (_parent->kv.first > kv.first) {
			_parent->left = cur;
			cur->parent = _parent;
		}
		else {
			_parent->right = cur;
			cur->parent = _parent;
		}
		Node* newNode = cur;	//cur在执行下面第二点的代码时,有可能会改变cur节点所以先保留下来
		
		//第二点:检测新节点插入后,红黑树的性质是否造到破坏
		
		root->col = BLACK;//在执行第二点的代码时有可能将根节点的颜色改变,所以在最后将根节点的颜色改为黑色即可。
		return make_pair(newNode, true);
	}
	
private:
	Node* root=nullptr;
};

3.5.2 检测新节点插入后,红黑树的性质是否造到破坏

因为新节点的默认颜色是红色,因此:如果双亲节点的颜色是黑色,没有违反红黑树任何性质,不需要做任何调整;但是当新插入节点的双亲节点颜色是红色时:就违反了性质3(不能有连续的两个红节点):此时要分类讨论:

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点。

以下图中:我省略了当前节点指向父亲节点的指针,只保留了当前节点指向左右子节点的指针。对于下面的图中有可能是一棵子树也有可能是一棵完整的树。

以下代码中:我以父节点为祖父节点的左节点为例子。

情况1:cur为红,p为红,g为黑,u存在且为红。

在这里插入图片描述

C++代码:
	Node* uncle = grandfather->right;
	if (uncle && uncle->col == RED) {
		//调整颜色
		_parent->col = uncle->col = BLACK;
		grandfather->col = RED;
		//继续向上调整
		cur = grandfather;
		_parent = cur->parent;
	}	
情况2:cur为红,p为红,g为黑,u不存在或u存在且为黑。

cur的情况一:1、当p为g的左子树,cur为p的左子树。2、当p为g的右子树,cur为p的右子树。

在这里插入图片描述
在这里插入图片描述

cur的情况二:1、当p为g的左子树,cur为p的右子树。2、当p为g的右子树,cur为p的左子树。

在这里插入图片描述

C++代码
	else {
		//cur为情况一:
		if (cur == _parent->left) {
			RotateR(grandfather);
			_parent->col = BLACK;
			grandfather->col = RED;
		}
		else {	//cur为情况二:
			RotateL(_parent);
			RotateR(grandfather);
			cur->col = BLACK;
			grandfather->col = RED;
		}
		break;	//不需要再向上调整了
	}
	//旋转的代码,其中旋转的逻辑和AVLTree的旋转是相同的,只是这里面没有对平衡因子的调节,其他逻辑全都是一样的
	void RotateR(Node* _parent) {
		Node* subL = _parent->left;
		Node* subLR = subL->right;

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

		Node* parentParent = _parent->parent;

		subL->right = _parent;
		_parent->parent = subL;

		if (_parent == root) {
			root = subL;
		}
		else {
			if (_parent == parentParent->left) {
				parentParent->left = subL;
			}
			else {
				parentParent->right = subL;
			}
		}
		subL->parent = parentParent;
	}

	void RotateL(Node* _parent) {
		Node* subR = _parent->right;
		Node* subRL = subR->left;

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

		Node* parentParent = _parent->parent;

		subR->left = _parent;
		_parent->parent = subR;

		if (_parent == root) {
			root = subR;
		}
		else {
			if (parentParent->left == _parent) {
				parentParent->left = subR;
			}
			else {
				parentParent->right = subR;
			}
		}
		subR->parent = parentParent;
	}
调整颜色和旋转的整体代码
	while (_parent && _parent->col == RED) {
			Node* grandfather = _parent->parent;
			//父节点为祖父节点的左子树
			if (grandfather->left == _parent) {	
				Node* uncle = grandfather->right;

				if (uncle && uncle->col == RED) {
					_parent->col = uncle->col = BLACK;
					grandfather->col = RED;
					cur = grandfather;
					_parent = cur->parent;
				}
				else {
					if (cur == _parent->left) {
						RotateR(grandfather);
						_parent->col = BLACK;
						grandfather->col = RED;
					}
					else {
						RotateL(_parent);
						RotateR(grandfather);
						cur->col = BLACK;
						grandfather->col = RED;
					}
					break;
				}
			}
			else {	//父节点为祖父节点的右子树
				Node* uncle = grandfather->left;
				if (uncle && uncle->col == RED) {
					_parent->col = uncle->col = BLACK;
					grandfather->col = RED;	
					cur = grandfather;
					_parent = cur->parent;
				}
				else {
					if (_parent->right == cur) {
						RotateL(grandfather);
						grandfather->col = RED;
						_parent->col = BLACK;
					}
					else {
						RotateR(_parent);
						RotateL(grandfather);
						cur->col = BLACK;
						grandfather->col = RED;
					}
					break;
				}
			}
		}
	}

	void RotateR(Node* _parent) {
		Node* subL = _parent->left;
		Node* subLR = subL->right;

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

		Node* parentParent = _parent->parent;

		subL->right = _parent;
		_parent->parent = subL;

		if (_parent == root) {
			root = subL;
		}
		else {
			if (_parent == parentParent->left) {
				parentParent->left = subL;
			}
			else {
				parentParent->right = subL;
			}
		}
		subL->parent = parentParent;
	}

	void RotateL(Node* _parent) {
		Node* subR = _parent->right;
		Node* subRL = subR->left;

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

		Node* parentParent = _parent->parent;

		subR->left = _parent;
		_parent->parent = subR;

		if (_parent == root) {
			root = subR;
		}
		else {
			if (parentParent->left == _parent) {
				parentParent->left = subR;
			}
			else {
				parentParent->right = subR;
			}
		}
		subR->parent = parentParent;
	}
插入节点的整体代码
	pair<Node*, bool> insert(const pair<K, V>& kv) {
		if (root == nullptr) {
			root = new Node(kv);
			root->col = BLACK;
			return make_pair(root, true);
		}
		Node* _parent = nullptr;
		Node* cur = root;
		while (cur) {
			if (cur->kv.first > kv.first) {
				_parent = cur;
				cur = cur->left;
			}
			else if (cur->kv.first < kv.first) {
				_parent = cur;
				cur = cur->right;
			}
			else {
				return make_pair(cur, false);
			}
		}
		cur = new Node(kv);
		if (_parent->kv.first > kv.first) {
			_parent->left = cur;
			cur->parent = _parent;
		}
		else {
			_parent->right = cur;
			cur->parent = _parent;
		}
		Node* newNode = cur;

		while (_parent && _parent->col == RED) {
			Node* grandfather = _parent->parent;
			if (grandfather->left == _parent) {
				Node* uncle = grandfather->right;

				if (uncle && uncle->col == RED) {
					_parent->col = uncle->col = BLACK;
					grandfather->col = RED;
					cur = grandfather;
					_parent = cur->parent;
				}
				else {
					if (cur == _parent->left) {
						RotateR(grandfather);
						_parent->col = BLACK;
						grandfather->col = RED;
					}
					else {
						RotateL(_parent);
						RotateR(grandfather);
						cur->col = BLACK;
						grandfather->col = RED;
					}
					break;
				}
			}
			else {
				Node* uncle = grandfather->left;
				if (uncle && uncle->col == RED) {
					_parent->col = uncle->col = BLACK;
					grandfather->col = RED;	
					cur = grandfather;
					_parent = cur->parent;
				}
				else {
					if (_parent->right == cur) {
						RotateL(grandfather);
						grandfather->col = RED;
						_parent->col = BLACK;
					}
					else {
						RotateR(_parent);
						RotateL(grandfather);
						cur->col = BLACK;
						grandfather->col = RED;
					}
					break;
				}
			}
		}
		root->col = BLACK;
		return make_pair(newNode, true);
	}

	void RotateR(Node* _parent) {
		Node* subL = _parent->left;
		Node* subLR = subL->right;

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

		Node* parentParent = _parent->parent;

		subL->right = _parent;
		_parent->parent = subL;

		if (_parent == root) {
			root = subL;
		}
		else {
			if (_parent == parentParent->left) {
				parentParent->left = subL;
			}
			else {
				parentParent->right = subL;
			}
		}
		subL->parent = parentParent;
	}

	void RotateL(Node* _parent) {
		Node* subR = _parent->right;
		Node* subRL = subR->left;

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

		Node* parentParent = _parent->parent;

		subR->left = _parent;
		_parent->parent = subR;

		if (_parent == root) {
			root = subR;
		}
		else {
			if (parentParent->left == _parent) {
				parentParent->left = subR;
			}
			else {
				parentParent->right = subR;
			}
		}
		subR->parent = parentParent;
	}

3.5 RBTree的验证

红黑树的验证分为两步:

1、检测其是否满足二叉搜索树(中序遍历有序)

bool IsOrder(Node* cur,Node* &prev){
	if(!root) return true;
	if(!IsOrder(cur->left,prev)) return false;
	if(!prev) prev=cur;
	else{
		if(prev->val>=cur->val) return false;
		prev=cur;
	}
	if(!IsOrder(cur->right,prev)) return false;
	return true;
}

2、 检测其是否满足红黑树的性质

	bool _CheckRedCol(Node* root) {
		if (!root)return true;
		if (root->col == RED && root->parent->col==RED) {
			cout<<"违反规则:不能出现两个连续的红节点"<<endl;
			return false;
		}
		return _CheckRedCol(root->left)&&_CheckRedCol(root->right);
	}
	bool _CheckBlackNum(Node* root,int &prev,int curNum) {
		if (!root) {
			if (!prev) {
				prev = curNum;
			}
			else {
				//检测每条路径上的节点的黑色节点是否相同
				return prev == curNum;
			}
			return true;
		}
		if (root->col == BLACK) curNum++;
		return _CheckBlackNum(root->left, prev, curNum) && _CheckBlackNum(root->right, prev, curNum);
	}
	bool IsBalance() {
		if (root && root->col == RED) {
			cout << "违反规则:根节点是黑色" << endl;
			return false;
		}
		int prev = 0;
		return _CheckRedCol(root) && _CheckBlackNum(root,prev,0);
	}

3.6 RBTree与AVLTree的比较

RBTree和AVLTree都是高效的平衡二叉搜索树,增删查改的时间复杂度都是O(log2N),红黑树不追求绝对的平衡,其只需保证最长路径不超过最短路径的两倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVLTree更优,而且红黑树实现比较简单,所以实际运用过程中红黑树更多。

  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值