C++_红黑树树

1红黑树的概念

AVL树和红黑树的比较

想出AVL树的人时大佬,想出红黑树的人是天才

他俩的性能是同一量级的,但是AVL树的严格平衡时要付出代价的,插入和删除的时候会大量旋转

AVL树维持那么高的精度是没有必要的

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

2 红黑树的性质和定义

  1. 每个结点不是红色就是黑色

  2. 根节点是黑色的

  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的

  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点

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

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

原因:当一条路径全为黑色的时候是最短的,当红与黑交替出现的时候是最大的,而且这两种情况当中黑色都个数都是相同的,那么极端情况下是二倍关系

先来复习一下枚举

C enum(枚举) | 菜鸟教程

3 红黑树节点的插入

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

  1. 按照二叉搜索的树规则插入新节点

  2. 检测新节点插入后,红黑树的性质是否造到破坏 因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何 性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连 在一起的红色节点,此时需要对红黑树分情况来讨论: 约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

红黑树的插入要分三种情况

1,uncle 存在且为红

那么就让uncle和parent都变黑,让grandfather变红,然后cur到grandfather的位置继续向上判断

继续向上处理的条件:

1 grandfather没有父亲,就是根,grandfather变黑即可

2 grandfather有父亲,父亲是黑,结束了

3 grandfather由父亲,父亲是红,跟刚才是类似的问题去处理

2,uncle 不存在

那么这时候就要去旋转即可,根据三者位置关系决定是左单旋还是右单旋还是双旋,旋转之后,让parent为红,grandfather为黑,就满足红黑树的要求了(旋转+变色)

3 ,uncle 存在且为黑

这时候单旋转和变色都不能解决问题了,要两个一起使用(旋转+变色)

抽象图

注意这里不是以高度来论, 而是黑节点的个数

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

情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑

这里就是双旋

插入代码

cpp 复制代码
bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_col = BLACK;
		return true;
	}
	Node* cur = _root;
	Node* parent = nullptr;
	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;
	}
	else
	{
		parent->_left = cur;
	}
	cur->_parent = parent;
	while(parent && parent->_col ==RED)
	{
		//parent为红,要去看uncle,注意这里一定会有grandfather
		Node* grandfather = parent->_parent;
		if (grandfather->_left == parent)
		{
			Node* uncle = grandfather->_right;
			//1如果uncle存在且为红
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				cur = grandfather;
				parent = cur->_parent;//若parent 为空那么grandfather就是根要将根置黑
				//而且还要继续
			}
			else
			{
				//2uncle不存在/或者为黑
				//这时候就要旋转了,根据g p c 的位置直接旋转
				if (parent->_left == cur)
				{
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
					
				}
				break;
			}
		}
		else//grandfather->_right == parent
		{
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;
				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				//2uncle不存在/或者为黑
				//这时候就要旋转了,根据g p c 的位置直接旋转
				if (parent->_right == cur)
				{
					RotateL(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					RotateR(parent);
					RotateL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;
			}


		}
	}
	_root->_col = BLACK;

	return true;
}

4红黑树的检查

1检查是否有两个连续的红节点

2每条路径黑色节点的数量是否一致

cpp 复制代码
//红黑树的验证有两点,1不能有连续的红节点,2所有路径的黑节点数量相同
bool _isBalance(Node* root)
{

	if (root == nullptr)
	{
		return true;
	}
	//计算出一个基准值
	int basline = 0;
	Node* cur = root;
	while (cur)
	{
		if (cur->_col == BLACK)
			++basline;
		cur = cur->_left;
	}

	return Checkcolor(root,0, basline);

}

bool Checkcolor(Node* root,int blacknum,int basline)
{
	if (root == nullptr)
	{
		if (blacknum != basline)
		{
			return false;
		}
		return true;
	}
	if (root->_col == BLACK)
		++blacknum;

	if (root->_col == RED && root->_parent && root->_parent->_col == RED)
	{
		return false;
	}

	return Checkcolor(root->_left,blacknum,basline) 
		&& Checkcolor(root->_right, blacknum, basline);
}

5 红黑树模拟实现STL中的map与set

底层都是用红黑树,map和set都是对红黑树的封装

5.1 红黑树的迭代器

迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代 器,需要考虑以前问题:

begin()与end() STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后, 可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位 置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块? 能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行--操作,必须要能找最 后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置:

1右不为空, 访问右树的最左节点

2右为空,(1如果child是parent的左访问parent,2如果child是parent的右,此时说明父亲已经访问里,就去访问祖先)

代码展示:

cpp 复制代码
template <class T, class Ptr, class Ref>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T, Ptr, Ref> Self;
	typedef __TreeIterator<T, T*, T&> Iterator;
	Node* _node;
	
	__TreeIterator(Node* node)
		:_node(node)
	{

	}
	//这个构造是为了让普通迭代器构造一个const迭代器
	__TreeIterator(const Iterator& it)
		:_node(it._node)
	{

	}

	Ref operator* ()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}

	//--的操作与加加相反
	Self& operator--()
	{
		if (_node->_left)
		{
			//找到左树的最大
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}
			_node = subRight;
		}
		else
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while (parent && cur ==parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}


	Self& operator++()
	{
		if (_node->_right)
		{
			//找到右树的最小
			Node* subLeft = _node->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}
			_node = subLeft;
		}
		else
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while(parent && cur == parent->_right)
			{ 
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}
};

5.2set

cpp 复制代码
#include "RBTree.h"
namespace cy
{
	template<class K>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		//普通迭代器和const迭代器都不能修改
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

		const_iterator begin() const
		{
			return _t.begin();
		}
		const_iterator end() const
		{
			return _t.end();
		}

		pair<iterator,bool> insert(const K& key)
		{
			//这里pair<iterator,bool> Insert返回的是普通迭代器RBTree::iterator
			//但是在set这一层iterator 是const_iterator
			//在类模板里面取内嵌类型要加typename
			pair<typename RBTree<K,K,SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
			return pair<iterator, bool>(ret.first, ret.second);
		}
	private:
		RBTree<K, K, SetKeyOfT> _t;
		
	};

}

5.3map

cpp 复制代码
#include "RBTree.h"
namespace cy
{
	template<class K,class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}

		const_iterator begin()const
		{
			return _t.begin();
		}
		const_iterator end()const
		{
			return _t.end();
		}
		pair<iterator,bool> insert(const pair<K, V>& kv)
		{
			return _t.Insert(kv);
		}
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		RBTree<K, pair<const K,V>, MapKeyOfT> _t;

	};

}

底层红黑树代码

cpp 复制代码
#pragma once

enum Color
{
	RED,
	BLACK
};

template <class T>
struct RBTreeNode
{
	RBTreeNode(const T& data)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_data(data)
		,_col(RED)
	{ }
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Color _col;
	T _data;
};

template <class T, class Ptr, class Ref>
struct __TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T, Ptr, Ref> Self;
	typedef __TreeIterator<T, T*, T&> Iterator;
	Node* _node;
	
	__TreeIterator(Node* node)
		:_node(node)
	{

	}
	//这个构造是为了让普通迭代器构造一个const迭代器
	__TreeIterator(const Iterator& it)
		:_node(it._node)
	{

	}

	Ref operator* ()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}

	//--的操作与加加相反
	Self& operator--()
	{
		if (_node->_left)
		{
			//找到左树的最大
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}
			_node = subRight;
		}
		else
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while (parent && cur ==parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}


	Self& operator++()
	{
		if (_node->_right)
		{
			//找到右树的最小
			Node* subLeft = _node->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}
			_node = subLeft;
		}
		else
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while(parent && cur == parent->_right)
			{ 
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}
};

template <class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
	
public:
	typedef __TreeIterator<T,T*, T&> iterator;
	typedef __TreeIterator<T, const T*, const T&> const_iterator;
	iterator begin()
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}
		return iterator(leftMin);
	}
	iterator end()
	{
		return iterator(nullptr);
	}
	const_iterator begin()const
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}
		return const_iterator(leftMin);
	}
	const_iterator end()const
	{
		return const_iterator(nullptr);
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		KeyOfT kot;
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else 
			{
				return cur;
			}
		}
		return nullptr;
	} 

	pair<iterator, bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);
		}
		Node* cur = _root;
		Node* parent = nullptr;
		KeyOfT kot;
		while (cur)
		{
			if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(iterator(cur), false);
			}
		}
		//找到插入的位置
		cur = new Node(data);
		cur->_col = RED;
		Node* newnode = cur;
		if (kot(parent->_data) < kot(data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;
		while(parent && parent->_col ==RED)
		{
			//parent为红,要去看uncle,注意这里一定会有grandfather
			Node* grandfather = parent->_parent;
			if (grandfather->_left == parent)
			{
				Node* uncle = grandfather->_right;
				//1如果uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;//若parent 为空那么grandfather就是根要将根置黑
					//而且还要继续
				}
				else
				{
					//2uncle不存在/或者为黑
					//这时候就要旋转了,根据g p c 的位置直接旋转
					if (parent->_left == cur)
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
						
					}
					break;
				}
			}
			else//grandfather->_right == parent
			{
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					//2uncle不存在/或者为黑
					//这时候就要旋转了,根据g p c 的位置直接旋转
					if (parent->_right == cur)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}


			}
		}
		_root->_col = BLACK;

		return make_pair(iterator(newnode), true);
	}

	void RotateL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = cur->_left;

		parent->_right = curleft;
		if (curleft)
		{
			curleft->_parent = parent;
		}
		cur->_left = parent;
		Node* pparent = parent->_parent;//先把父亲存起来
		parent->_parent = cur;
		if (pparent)//不为空说明是子树,parent不是根节点
		{
			if (pparent->_right == parent)
			{
				pparent->_right = cur;
			}
			else
			{
				pparent->_left = cur;
			}
			cur->_parent = pparent;
		}
		else
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		
	}

	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;

		parent->_left = curright;
		if (curright)
		{
			curright->_parent = parent;
		}
		cur->_right = parent;
		Node* pparent = parent->_parent;
		parent->_parent = cur;
		if (pparent)
		{
			if (pparent->_right == parent)
			{
				pparent->_right = cur;
			}
			else
			{
				pparent->_left = cur;
			}
			cur->_parent = pparent;
		}
		else
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		
	}
	bool isBalance()
	{
		return _isBalance(_root);
	}

	//红黑树的验证有两点,1不能有连续的红节点,2所有路径的黑节点数量相同
	bool _isBalance(Node* root)
	{

		if (root == nullptr)
		{
			return true;
		}
		//计算出一个基准值
		int basline = 0;
		Node* cur = root;
		while (cur)
		{
			if (cur->_col == BLACK)
				++basline;
			cur = cur->_left;
		}

		return Checkcolor(root,0, basline);

	}

	bool Checkcolor(Node* root,int blacknum,int basline)
	{
		if (root == nullptr)
		{
			if (blacknum != basline)
			{
				return false;
			}
			return true;
		}
		if (root->_col == BLACK)
			++blacknum;

		if (root->_col == RED && root->_parent && root->_parent->_col == RED)
		{
			return false;
		}

		return Checkcolor(root->_left,blacknum,basline) 
			&& Checkcolor(root->_right, blacknum, basline);
	}

	int Height()
	{
		return Height(_root);
	}

	int Height(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		int Heightleft = Height(root->_left);
		int Heightright = Height(root->_right);
		return Heightleft > Heightright ? Heightleft + 1 : Heightright + 1;
	}

private:
	Node* _root=nullptr;
};
相关推荐
绿炮火6 分钟前
【MATLAB】(一)简介
开发语言·数学建模·matlab
超浪的晨7 分钟前
JavaWeb 进阶:Vue.js 与 Spring Boot 全栈开发实战(Java 开发者视角)
java·开发语言·前端·javascript·vue.js·html·个人开发
醉卧红尘的鱼11 分钟前
最优估计准则与方法(5)加权最小二乘估计(WLS)_学习笔记
学习·算法
她说人狗殊途17 分钟前
Map 集合
java·开发语言·算法
命苦的孩子17 分钟前
List 接口
java·数据结构·list·intellij-idea
EndingCoder1 小时前
Three.js + AI:结合 Stable Diffusion 生成纹理贴图
开发语言·前端·javascript·人工智能·stable diffusion·ecmascript·three.js
leafpipi1 小时前
【机器学习】pycharm使用SSH SFTP 远程连接 ubuntu服务器 进行开发+调试+数据训练
服务器·学习·算法·ubuntu·pycharm·ssh
姜不吃葱1 小时前
【力扣热题100】哈希——最长连续序列
算法·leetcode·哈希算法
百度Geek说1 小时前
直击WAIC | 百度袁佛玉:加速具身智能技术及产品研发,助力场景应用多样化落地
算法
Chef_Chen1 小时前
从0开始学习R语言--Day62--RE插补
开发语言·学习·r语言