红黑树(RBTree)

RBTree

  • [1. 红黑树的概念和性质](#1. 红黑树的概念和性质)
  • [2. 红黑树的实现](#2. 红黑树的实现)
    • [2.1 结点的定义](#2.1 结点的定义)
    • [2.2 插入](#2.2 插入)
    • [2.3 判断红黑树](#2.3 判断红黑树)
    • [2.4 其他接口](#2.4 其他接口)
  • [3. 迭代器](#3. 迭代器)
  • [4. 完整代码](#4. 完整代码)

1. 红黑树的概念和性质

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或

Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

即:最短路径的长度*2>=最长路径的长度

满足以下几点性质的树为红黑树。

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

2. 红黑树的实现

2.1 结点的定义

相比普通树的结点,增加了一个父指针,指向父亲。增加了一个常量表示颜色。

cpp 复制代码
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode(const pair<K, V>& kv, Color col = RED)
		:_kv(kv),
		_left(nullptr),
		_right(nullptr),
		_parent(nullptr),
		_col(col)
	{}

	pair<K, V> _kv;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Color _col;
};

通过一个pair对象和col构造一个Node,这里存储的数据依旧是键值对的形式。

对于颜色,只有两种。采用枚举的形式。

cpp 复制代码
enum Color
{
	RED,
	BLACK
};

2.2 插入

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

  1. 按照二叉搜索的树规则插入新节点
  2. 检测新节点插入后,红黑树的性质是否造到破坏

插入结点时,颜色默认给红色。如果给黑色,会导致某一条路径的黑色结点数增加,就需要修改其他所有路径的黑色结点。如果给红色,可能出现连续的红色结点,也需要修改。但是这个随机的,并不是每次插入都需要修改。

即:颜色为黑色,一定修改。颜色为红色,可能修改。

插入的部分代码

cpp 复制代码
bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv,BLACK); //根结点为黑色
		return true;
	}
	Node* cur = _root;
	Node* parent = _root;
	while (cur)
	{
		if (kv.first > cur->_kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (kv.first < cur->_kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}

	cur = new Node(kv); //默认插入结点为红色

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

	while (parent && parent->_col==RED) //出现连续的红色结点
	{
		//...
		//...
	}

	_root->_col = BLACK; // 可能会修改到根节点的颜色

	return true;
}

颜色的修改。

上图中树可以是子树也可以是整棵树。

情况一:只需要将p,u变黑,g变红即可。改变前后两条路径的黑色结点数没有改变。

g如果是根节点,将g变黑。

g如果不是根节点,需要判断g的父节点的颜色,如果为红色,需要继续调整。

情况二:旋转后进行变色

情况二可由情况一转变而来。旋转逻辑和AVL树的中的一致,分为单旋和双旋。只是这里没有了平衡因子。但增加了变色

cpp 复制代码
bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv,BLACK); //根结点为黑色
		return true;
	}
	Node* cur = _root;
	Node* parent = _root;
	while (cur)
	{
		if (kv.first > cur->_kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (kv.first < cur->_kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}

	//new结点并链接
	cur = new Node(kv); //默认插入结点为红色

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

	while (parent && parent->_col==RED)
	{
		Node* Pparent = parent->_parent;
		if (parent == Pparent->_left)               
		{                                                   
			Node* uncle = Pparent->_right;
			if (uncle && uncle->_col == RED)  // uncle存在且为红:变色,然后向上继续处理
			{
				uncle->_col = parent->_col = BLACK;
				Pparent->_col = RED;

				cur = Pparent;
				parent = cur->_parent;
			}
			else   // uncle不存在或存在且为黑:旋转+变色处理
			{
				if (cur == parent->_left)   //单旋                    
				{                                      
					RotateRight(Pparent);                            
					parent->_col = BLACK;
					Pparent->_col = RED;
				}
				else                        //双旋       
				{
					RotateLeft(parent);
					RotateRight(Pparent);
					cur->_col = BLACK;
					Pparent->_col = RED;
				}
				break;
			}
		}
		else                                  
		{
			Node* uncle = Pparent->_left;

			if (uncle && uncle->_col == RED)
			{
				uncle->_col = parent->_col = BLACK;
				Pparent->_col = RED;

				cur = Pparent;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_right)
				{
					RotateLeft(Pparent);
					parent->_col = BLACK;
					Pparent->_col = RED;
				}
				else
				{
					RotateRight(parent);
					RotateLeft(Pparent);
					cur->_col = BLACK;
					Pparent->_col = RED;
				}
				break;
			}
		}
	}

	_root->_col = BLACK;
	return true;
}

情况一可能需要向上调整,情况二只需要调整一次。

2.3 判断红黑树

通过红黑树的性质判断,满足四条性质的树为红黑树。

  1. 先计算一条路径的黑色结点,在判断其他路径的黑色结点数是否相等。
  2. 根据存在的父节点和孩子结点的颜色和判断是否有连续的红色结点。
cpp 复制代码
bool IsRBTree()
{
	return _IsRBTree(_root);
}

bool _RBTree(Node* root, int num, int BlackCount)
{
	if (root == nullptr) //一条路径走完后
	{
		if (num == BlackCount)
			return true;
		else
		{
			cout << "两条路径黑色结点个数不同" << endl;
			return false;
		}
	}

	if (root->_col == BLACK)
		num++;
	else
	{
		if (root->_parent && root->_parent->_col == RED)
		{
			cout << "出现连续红色结点" << endl;
			return false;
		}
	}

	return _RBTree(root->_left, num, BlackCount) && _RBTree(root->_right, num, BlackCount);
}

bool _IsRBTree(Node* root)
{
	if (root == nullptr)
		return true;

	if (root->_col == RED)
	{
		cout << "根为红色" << endl;
		return false;
	}

	//统计一条路径中黑色结点的个数
	int count = 0;
	Node* cur = root;
	while (cur)
	{
		if(cur->_col==BLACK) 
			count++;
		cur = cur->_left;
	}
	
	int num = 0;
	return _RBTree(root, num, count);
}

2.4 其他接口

其他接口,包括构造,析构,拷贝等和AVL树类似。

3. 迭代器

容器需要遍历,那么就要实现迭代器。

代码如下,其中,operator++ 的实现比较复杂。

cpp 复制代码
template<class T,class V>
struct RBTreeIterator
{
	typedef RBTreeNode<T,V> Node;
	typedef RBTreeIterator<T,V> Self;

	Node* _node;

	RBTreeIterator(Node* node)
		:_node(node)
	{}

	Self& operator++()
	{
		
	}

	pair<T,V>* operator->()
	{
		return &_node->_kv;
	}

	pair<T, V>& operator*()
	{
		return _node->_kv;
	}

	bool operator!=(const Self& s) const
	{
		return _node != s._node;
	}
	bool operator==(const Self& s) const
	{
		return _node == s._node;
	}

};
cpp 复制代码
Self& operator++()
{
	Node* cur = _node;
	Node* parent = _node->_parent;

	if (cur->_right) //右子树不为空,找右子树的最左结点
	{
		Node* left = cur->_right;
		while (left)
		{
			cur = left;
			left = cur->_left;
		}
		_node = cur;
	}
	else        
	{
		while (parent && cur == parent->_right)
		{
			cur = parent;
			parent = cur->_parent;
		}
		_node = parent;
	}
	
	return *this;
}

测试迭代器和插入

测试红黑树的判断

4. 完整代码

RBTree.h

cpp 复制代码
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

enum Color
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	RBTreeNode(const pair<K, V>& kv, Color col = RED)
		:_kv(kv),
		_left(nullptr),
		_right(nullptr),
		_parent(nullptr),
		_col(col)
	{}

	pair<K, V> _kv;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Color _col;
};

template<class T,class V>
struct RBTreeIterator
{
	typedef RBTreeNode<T,V> Node;
	typedef RBTreeIterator<T,V> Self;

	Node* _node;

	RBTreeIterator(Node* node)
		:_node(node)
	{}

	Self& operator++()
	{
		Node* cur = _node;
		Node* parent = _node->_parent;

		if (cur->_right) //右子树不为空,找右子树的最左结点
		{
			Node* left = cur->_right;
			while (left)
			{
				cur = left;
				left = cur->_left;
			}
			_node = cur;
		}
		else        
		{
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}
			_node = parent;
		}
		
		return *this;
	}

	pair<T,V>* operator->()
	{
		return &_node->_kv;
	}

	pair<T, V>& operator*()
	{
		return _node->_kv;
	}

	bool operator!=(const Self& s) const
	{
		return _node != s._node;
	}
	bool operator==(const Self& s) const
	{
		return _node == s._node;
	}

};

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

	typedef RBTreeIterator<K, V> Iterator;

	Iterator Begin() //找最左结点
	{
		Node* cur = _root;
		Node* left = cur->_left;
		while (left)
		{
			cur = left;
			left = cur->_left;
		}
		return Iterator(cur);
	}

	Iterator End()
	{
		return Iterator(nullptr);
	}

	RBTree() = default;

	RBTree(const RBTree& a)
	{
		_root = Copy(a._root);
	}

	Node* operator=(RBTree a)
	{
		swap(_root, a._root);
		return _root;
	}


	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv,BLACK); //根结点为黑色
			return true;
		}
		Node* cur = _root;
		Node* parent = _root;
		while (cur)
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		//new结点并链接
		cur = new Node(kv); //默认插入结点为红色

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

		while (parent && parent->_col==RED)
		{
			Node* Pparent = parent->_parent;
			if (parent == Pparent->_left)               //    Pp
			{                                                     //  p         u
				Node* uncle = Pparent->_right;
				if (uncle && uncle->_col == RED)  // uncle存在且为红:变色,然后向上继续处理
				{
					uncle->_col = parent->_col = BLACK;
					Pparent->_col = RED;

					cur = Pparent;
					parent = cur->_parent;
				}
				else											// uncle不存在或存在且为黑:旋转+变色处理
				{
					if (cur == parent->_left)   //单旋       //      Pp                     p
					{                                                    //    p     u    --->     c    Pp
						RotateRight(Pparent);                  // c                                    u
						parent->_col = BLACK;
						Pparent->_col = RED;
					}
					else                                 //双旋       
					{
						RotateLeft(parent);
						RotateRight(Pparent);
						cur->_col = BLACK;
						Pparent->_col = RED;
					}
					break;
				}
			}
			else                                  
			{
				Node* uncle = Pparent->_left;

				if (uncle && uncle->_col == RED)
				{
					uncle->_col = parent->_col = BLACK;
					Pparent->_col = RED;

					cur = Pparent;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						RotateLeft(Pparent);
						parent->_col = BLACK;
						Pparent->_col = RED;
					}
					else
					{
						RotateRight(parent);
						RotateLeft(Pparent);
						cur->_col = BLACK;
						Pparent->_col = RED;
					}
					break;
				}
			}
		}

		_root->_col = BLACK;

		return true;
	}

	bool Find(const K& val)
	{
		Node* cur = _root;
		while (cur)
		{
			if (val > cur->_kv.first)
				cur = cur->_right;
			else if (val < cur->_kv.first)
				cur = cur->_left;
			else
				return true;
		}
		return false;
	}

	void Inorder()
	{
		_Inorder(_root);
		cout << endl;
	}

	bool IsRBTree()
	{
		return _IsRBTree(_root);
	}

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

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

private:

	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;
		Node* newNode = new Node(root->_kv,root->_col);
		newNode->_left = Copy(root->_left);
		newNode->_right = Copy(root->_right);

		if (newNode->_left)
			newNode->_left->_parent = newNode;
		if (newNode->_right)
			newNode->_right->_parent = newNode;

		return newNode;
	}

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

		Destroy(root->_left);
		Destroy(root->_right);

		delete root;
		root = nullptr;
	}

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

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

		return left > right ? left + 1 : right + 1;
	}

	bool _RBTree(Node* root, int num, int BlackCount)
	{
		if (root == nullptr) //一条路径走完后
		{
			if (num == BlackCount)
				return true;
			else
			{
				cout << "两条路径黑色结点个数不同" << endl;
				return false;
			}
		}

		if (root->_col == BLACK)
			num++;
		else
		{
			if (root->_parent && root->_parent->_col == RED)
			{
				cout << "出现连续红色结点" << endl;
				return false;
			}
		}

		return _RBTree(root->_left, num, BlackCount) && _RBTree(root->_right, num, BlackCount);
	}

	bool _IsRBTree(Node* root)
	{
		if (root == nullptr)
			return true;

		if (root->_col == RED)
		{
			cout << "根为红色" << endl;
			return false;
		}

		//统计一条路径中黑色结点的个数
		int count = 0;
		Node* cur = root;
		while (cur)
		{
			if(cur->_col==BLACK) 
				count++;
			cur = cur->_left;
		}
		
		int num = 0;
		return _RBTree(root, num, count);
	}

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

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

		parent->_parent = subR;
		subR->_parent = Pparent;

		if (subRL)
			subRL->_parent = parent;

		if (Pparent == nullptr) //说明parent结点为原来的根
			_root = subR;
		else
		{
			if (Pparent->_left == parent)
				Pparent->_left = subR;
			else
				Pparent->_right = subR;
		}
	}

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

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

		parent->_parent = subL;
		subL->_parent = Pparent;

		if (subLR)
			subLR->_parent = parent;

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

	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;
};

test.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include"RBTree.h"


void TestRBTree()
{
	RBTree<int, int> r;
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	for (auto val : a)
	{
		r.Insert({ val,val });
		//cout << r.IsRBTree()<<endl<<endl;
	}

	r.Inorder();
	cout << r.IsRBTree() << endl;
	cout << r.Height() << endl;

	RBTree<int, int> r1(r);  /拷贝构造
	RBTree<int, int> r2;

	r2 = r;   //赋值

	cout << endl;


}

void TestRBTree1()
{
	RBTree<int, int> r;
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	for (auto val : a)
	{
		r.Insert({ val,val });
	}

	RBTree<int, int>::Iterator it = r.Begin();
	while (it != r.End())
	{
		cout << it->first << " ";
		cout << (*it).first << " ";
		++it;
	}

}
int main()
{
	TestRBTree();
	//TestRBTree1();
	return 0;
}
相关推荐
槿花Hibiscus2 小时前
C++基础:Pimpl设计模式的实现
c++·设计模式
黑不拉几的小白兔3 小时前
PTA部分题目C++重练
开发语言·c++·算法
写bug的小屁孩3 小时前
websocket身份验证
开发语言·网络·c++·qt·websocket·网络协议·qt6.3
chordful3 小时前
Leetcode热题100-32 最长有效括号
c++·算法·leetcode·动态规划
材料苦逼不会梦到计算机白富美3 小时前
线性DP 区间DP C++
开发语言·c++·动态规划
ahadee4 小时前
蓝桥杯每日真题 - 第12天
c++·vscode·算法·蓝桥杯
vortex54 小时前
解决 VSCode 中 C/C++ 编码乱码问题的两种方法
c语言·c++·vscode
醉颜凉6 小时前
【NOIP提高组】潜伏者
java·c语言·开发语言·c++·算法
hunandede6 小时前
FFmpeg 4.3 音视频-多路H265监控录放C++开发十三.2:avpacket中包含多个 NALU如何解析头部分析
c++·ffmpeg·音视频
爱学习的大牛1236 小时前
通过vmware虚拟机安装和调试编译好的 ReactOS
c++·windows内核