红黑树(Red BlackTree)的实现

本文详细介绍了红黑树的定义、性质、实现以及如何进行插入、删除、搜索等操作,强调了红黑树在保持平衡二叉查找树优势的同时,通过简单的规则实现了高效的动态集合操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      前面我们讲解了简单二叉查找树、AVL树,伸展树,对于一棵高度为h的二叉查找树,其动态集合操作Search、Minimum、Maximum、Successor、Predecessor、Insert、Delete的运行时间为θ(h),树的高度决定了在树上操作的成本。

一些常见的搜索树的高度:

平衡二叉搜索树:O(lgn)

1962年提出的AVL树:<=1.44lgn

1972年提出的红黑树:<=2lg(n+1)

      红黑树是通过对二叉查找树结点上增加一个存储位表示结点的颜色(红色或黑色) 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,确保没有一条路径会比其他路径长出两倍,从而接近平衡。

一、红黑树的性质

1、红黑树的定义

红黑树是满足如下性质的二叉查找树:

(1) 每个结点必须为红色或黑色。

(2) 根为黑色。

(3) 树中的nil叶子为黑色。

(4) 若结点为红,则其两个子孩子必为黑。

(5) 对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。

2、黑高度

从某个结点x出发(不包括该结点)到达一个叶结点的任意一条路径上,黑色结点的个数称为该结点x的黑高度,用bh(x)表示。

3、红黑树的黑高度

红黑树的黑高度定义为其根结点的黑高度,记为bh(root[T]).。

4、红黑树的高度

一棵含n个内结点的红黑树的高度至多为2lg( n+1) 。(证明请看算法导论)

二、红黑树的实现

1、原理请看参考资料[1]、[2]、[3]

2、下面给出的C++实现是完全按照算法导论上的伪代码来的。参考资料[4]中给出了自顶向下红黑树的部分实现(未提供删除操作),采用的是不含父节点指针的结点结构,但是实现复杂,不是很好理解。

3、实现代码

#include <cstdlib>
#include <iostream>

using namespace std;

template <typename Comparable>
class RBTree
{
	public:
		
		RBTree()
		{
			nil = new RBTreeNode;//哨兵结点为黑色 
			nil->parent = nil;
			nil->lchild = nil;
			nil->rchild = nil;
			root = nil;		
		}
		~RBTree()
		{
			makeEmpty(root);
			delete nil;
		}
		
		void preOrderTraverse()const
		{
			preOrderTraverse(root);	
		}
		
		void inOrderTraverse()const
		{
			inOrderTraverse(root);
		}
		
		void printTree()const
		{
			cout<<"preOrder :"<<endl;
			preOrderTraverse();
			cout<<endl;
			
			cout<<"inOrder :"<<endl;
			inOrderTraverse();
			cout<<endl<<endl;
		}
		
		//将元素插入红黑树中 
		bool insert(const Comparable& elem)
		{
			RBTreeNode* y = nil;	//y记录当前结点的父结点 
			RBTreeNode* x = root;		//从根结点开始扫描,寻找插入点 
			
			while(x != nil)
			{
				y = x;
				if(elem < x->data)	//当前结点左子树中继续扫描 
				{
					x = x->lchild;
				}
				else if(x->data < elem)
				{
					x = x->rchild;	//当前结点右子树中继续扫描 
				}
				else
				{
					return false;			//树中已存在该元素 
				}
			}
			//准备插入该元素 
			RBTreeNode* z = new RBTreeNode(elem);
			z->parent = y;					//y是z的双亲 
			if(y == nil)					//z插入空树 
			{
				root = z;						//z为新的根结点 
			}
			else if(elem < y->data)
			{
				y->lchild = z;				//z为y的左孩子 
			}
			else
			{
				y->rchild = z;				//z为y的右孩子 
			}
			z->lchild = nil;
			z->rchild = nil;
			z->color = RED;
			insertFixup(z);					//插入后调整 
			return true; 
		}
		
		//从红黑树中删除一个元素		
		bool remove(const Comparable& elem)
		{
			RBTreeNode* z = search(elem, root);//查找结点 
			RBTreeNode* y;		//y为要删除的结点
			RBTreeNode* x;		//y的孩子结点 
				
			if(z != nil)
			{
				if(z->lchild == nil || z->rchild == nil)//case1 2
				{
					y = z;
				}
				else	//左右子树均不空 case 3
				{
					y = successor(z);//后继y的左子树必然为空(nil) 
				}
				//确定y的孩子结点(待连接到p[y]) 
				if(y->lchild != nil)
				{
					x = y->lchild;
				}
				else
				{
					x = y->rchild;
				}
				
				x->parent = y->parent;
				if(y->parent == nil)//y为根结点 
				{
					root = x;
				}
				else if(y == y->parent->lchild)
				{
					y->parent->lchild = x;
				}
				else
				{
					y->parent->rchild = x;
				}
				
				if(y != z)//case 3
				{
					z->data = y->data;
				}
				
				if(y->color == BLACK)//删除结点为黑色,重新调整 
				{
					removeFixup(x);
				}
				
				delete y;
				return true;
			}
			return false;				//树中不存在该结点 
		}
		
		bool search(const Comparable& elem)const
		{
			if(search(elem, root)!= nil)
			{
				return true;
			}
			return false;
		}
		
		bool isEmpty()
		{
			return root == nil;
		}
		
		/*求某一元素的后继,前驱类似 
		 *应该确保树非空,且树中存在该元素 
		 否则抛出异常(这里不做处理) 
		 */
		Comparable successor(const Comparable& elem)
		{
			RBTreeNode* z = search(elem, root);//查找结点
			if(z != nil)
			{
				return z->data;
			}
//			else
//			{
//				throw exception;
//			}
		}
		
	private:
		
		enum COLOR{BLACK,RED};
		
		struct RBTreeNode
		{
			COLOR color;					//结点颜色 
			Comparable data;				//结点数据 
			struct RBTreeNode* parent;	//父节点指针 
			struct RBTreeNode* lchild;	//左孩子结点指针 
			struct RBTreeNode* rchild;	//右孩子结点指针 
			
			RBTreeNode(): color(BLACK),data(Comparable()),
							parent(NULL),lchild(NULL),rchild(NULL){}
			RBTreeNode(const Comparable& elem ): color(BLACK),data(elem),
							parent(NULL),lchild(NULL),rchild(NULL){}
		};
		//========================================
		RBTreeNode * root;				//根结点 
		RBTreeNode * nil;				//哨兵结点 
		//========================================
		
		void leftRotate(RBTreeNode* x)	//左旋 
		{
			RBTreeNode* y = x->rchild;
			x->rchild = y->lchild;
			if(y->lchild != nil)
			{
				y->lchild->parent = x;
			}
			y->parent = x->parent;
			if(x->parent == nil)
			{
				root = y;				//y为新的根结点 
			}
			else if(x == x->parent->lchild)
			{
				x->parent->lchild = y;
			}
			else
			{
				x->parent->rchild = y;
			}
			y->lchild = x;
			x->parent = y;
		}
		
		void rightRotate(RBTreeNode* x)//右旋 
		{
			RBTreeNode* y = x->lchild;
			x->lchild = y->rchild;
			if(y->rchild != nil)
			{
				y->rchild->parent = x;
			}
			y->parent = x->parent;
			if(x->parent == nil)
			{
				root = y;
			}
			else if(x == x->parent->lchild)
			{
				x->parent->lchild = y;
			}
			else
			{
				x->parent->rchild = y;
			}
			y->rchild = x;
			x->parent = y;
		}
		
		//插入之后的修复操作 
		void insertFixup(RBTreeNode* z)
		{ 
			RBTreeNode* y;				//y是z的叔父结点 
			while(z->parent->color == RED)	
			{
				//z的父结点是z的祖父结点的左孩子 
				if(z->parent == z->parent->parent->lchild)
				{
					//case 1 2 3
					y = z->parent->parent->rchild;
					if(y->color == RED)		
					{
						//case1 
						z->parent->color = BLACK;
						y->color = BLACK;
						z->parent->parent->color = RED;
						z = z->parent->parent;
					}
					else //case2 or case 3 ,y->color == BLACK
					{						
						//case2
						if(z == z->parent->rchild)
						{
							z = z->parent;		//z上溯至父节点 
							leftRotate(z);		//左旋
						}
						//case3
						z->parent->color = BLACK;
						z->parent->parent->color = RED;
						rightRotate(z->parent->parent);
					}							
				}
				else//case 4,5,6与上面对称 
				{
					y = z->parent->parent->lchild;
					if(y->color == RED)
					{
						//case4
						z->parent->color = BLACK;
						y->color = BLACK;
						z = z->parent->parent;
					}
					else//case 5 or case 6
					{
						//case 5
						if(z == z->parent->lchild)
						{
							z = z->parent;
							rightRotate(z);
						}
						//case 6
						z->parent->color = BLACK;
						z->parent->parent->color = RED;
						leftRotate(z->parent->parent);
					}
				}
			}
			root->color = BLACK;
		}		
		
		//删除之后的修复操作		
		void removeFixup(RBTreeNode* x)
		{
			RBTreeNode* w;			//x的兄弟结点 
			
			while(x != root && x->color == BLACK)
			{
				if(x == x->parent->lchild)//case1-4
				{
					w = x->parent->rchild;
					if(w->color == RED)
					{
						//case1
						w->color = BLACK;
						x->parent->color = RED;
						leftRotate(x->parent);
						w = x->parent->rchild;
					}
					
					//w->color == BLACK
					if(w->lchild->color == BLACK && w->rchild->color == BLACK)
					{
						//case 2
						w->color = RED;
						x = x->parent;//
					}
					else  
					{
						//case 3 
						if(w->rchild->color == BLACK)//左子为RED
						{							
							w->lchild->color = BLACK;
							w->color = RED;
							rightRotate(w);
							w = x->parent->rchild;
						}
						//case 4 右子为RED
						w->color = x->parent->color;
						x->parent->color = BLACK;
						w->rchild->color = BLACK;
						leftRotate(x->parent);
						x = root;
					}					
				}
				else//case5-8,和上面对称 
				{
					w = x->parent->lchild;
					if(w->color == RED)
					{
						//case 5
						w->color = BLACK;
						x->parent->color = RED;
						rightRotate(x->parent);
						w = x->parent->lchild;
					}
					
					//w->color == BLACK
					if(w->lchild->color == BLACK && w->rchild->color == BLACK)
					{
						//case 6
						w->color = RED;
						x = x->parent;//
					}
					else  
					{
						//case 7
						if(w->lchild->color == BLACK)//右子为RED
						{							
							w->rchild->color = BLACK;
							w->color = RED;
							leftRotate(w);
							w = x->parent->lchild;
						}
						//case 8 左子为RED
						w->color = x->parent->color;
						x->parent->color = BLACK;
						w->lchild->color = BLACK;
						rightRotate(x->parent);
						x = root;
					}
				}
			}
			
			x->color = BLACK;
		}
		
		//在以t为根的红黑树中查找给定元素
		//返回相应的结点指针 
		RBTreeNode* search(const Comparable& elem, RBTreeNode* t)const
		{
			while(t != nil && elem != t->data)
			{
				if(elem < t->data)
				{
					t = t->lchild;
				}
				else if(t->data < elem)
				{
					t = t->rchild;
				}
			}
			return t;
		}
		
		//求结点x的中序后继结点 
		RBTreeNode* successor(RBTreeNode* x)
		{
			RBTreeNode* y;
			
			if(x->rchild != nil)
			{
				y = x->rchild;
				while(y->lchild != nil)
				{
					y = y->lchild;
				}
			}
			else
			{
				y = x->parent;
				while(y != nil && x == y->rchild)
				{
					x = y;
					y = y->parent;
				}
			}
			return y;
		}
		
		//前序遍历 
		void preOrderTraverse(RBTreeNode* t)const
		{
			if(t != nil)
			{
				cout<<"("<<t->data<<",";
				t->color ?  cout<<"RED" : cout<<"BLACK";
				cout<<")";
				preOrderTraverse(t->lchild);
				preOrderTraverse(t->rchild);
			}
		}
		
		//中序遍历 
		void inOrderTraverse(RBTreeNode* t)const
		{
			if(t != nil)
			{				
				inOrderTraverse(t->lchild);
				cout<<"("<<t->data<<",";
				t->color ?  cout<<"RED" : cout<<"BLACK";
				cout<<")";
				inOrderTraverse(t->rchild);
			}
		}
		
		//销毁红黑树 
		void makeEmpty(RBTreeNode* t)
		{
			if(t != nil)
			{
				makeEmpty(t->lchild);
				makeEmpty(t->rchild);
				delete t;
				t = NULL;
			}
		}
};

int main(int argc, char *argv[])
{
	RBTree<int> rbtree;
	const int N = 6;
	//insert
	for(int i=1; i<N;i++)
	{
		rbtree.insert(i);
		cout<<"after insert "<<i<<":"<<endl;
		rbtree.printTree();
	}
	
	//search
	cout<<"search: "<<endl;
	for(int i=1; i<N;i++)
	{
		cout<<"("<<i<<","<<rbtree.search(i)<<")";
	}
	cout<<endl<<endl;
	
	//remove	
	for(int i=1; i<N; i += 2)
	{
		rbtree.remove(i);
		cout<<"after remove: "<<i<<endl;
		rbtree.printTree();
	}

	//search
	cout<<"search: "<<endl;	
	for(int i=1; i<N;i++)
	{
		cout<<"("<<i<<","<<rbtree.search(i)<<")";
	}
	cout<<endl<<endl;
	
    system("PAUSE");
    return EXIT_SUCCESS;
}

参考资料:

[1]算法导论(第2版)第13章P163-P176

[2]红黑树及扩张:http://wenku.baidu.com/view/673d3e6eb84ae45c3b358c29.html

[3]博客文章(C#实现):http://www.cnblogs.com/abatei/archive/2008/12/17/1356565.html

[4]Data Structures and Algorithm Analysis in C++(third editon) (数据结构与算法分析C++描述,第3版 )

[5]文章(C++实现):http://www.linuxidc.com/Linux/2012-01/52530.htm

[6]文章(C实现):http://www.chinaunix.net/old_jh/23/1308846.html

[7]C语言实现:http://wenku.baidu.com/view/1187190390c69ec3d5bb7544.html

[8]红黑树的实现与讨论:http://www.cppblog.com/daly88/archive/2009/11/20/101519.html

<think>首先,用户的问题基于系统级指令和我之前的回答结构。用户现在要求了解C++STL中map的底层实现原理。系统级指令的关键点:-使用$...$格式行内数学表达式。-独立公式使用$$...$$格式并单独成段。-确保LaTeX语法正确。-使用中文回答。-生成相关问题。-引用的段落末尾添加引用标识如[^1]。用户可见层指令:-回答结构清晰,帮助用户逐步解决问题。-保证回答真实可靠。用户的问题:"我想了解C++STL库中map的底层实现原理请问C++STLmap底层实现原理数据结构"从提供的引用中,我可以提取相关信息:-引用[^3]提到:"红黑树,平衡、有序二叉树。红黑树介绍https://www.iamshuaidi.com/2061.html。key和value存储在节点中。插入与删除主要是调整节点的指针。搜索$O(log(n))$"。-引用中还有map的常用函数和操作。-其他引用涉及deque、vector等,但用户指定了map。所以,map的底层实现通常基于红黑树Red-BlackTree),一种自平衡的二叉搜索树。我需要构建一个清晰、逐步的回答:1.**介绍map的基本概念**:STLmap是一个关联容器,提供键值对存储。2.**底层数据结构**:基于红黑树。3.**解释红黑树**:简要描述红黑树的性质和优点,如平衡树确保插入、删除、搜索时间复杂度为$O(logn)$。4.**详细实现**:包括节点结构、如何维护平衡(旋转、颜色变更)。5.**性能分析**:时间复杂度,优缺点。6.**引用相关部分**:在解释时,加入引用标识。结构布局:-开头简要回答。-然后详细分解。-结尾生成相关问题。确保使用LaTeX:-行内数学:如搜索时间复杂度$O(logn)$。-如果有公式,独立使用$$,但红黑树可能不需要复杂公式,除非描述树高。实际步骤:1.回答核心问题。2.基于引用[^3]展开。3.添加引用标识在引用的部分末尾。生成相关问题:-类似于示例,列出3-5个相关问题。完整回答结构:-第一部分:解释map的底层实现。-第二部分:生成相关问题。</think> ### C++ STL `map` 底层实现原理 C++ STL 中的 `map` 是一种关联容器,它基于键值对(key-value)存储数据,键(key)必须唯一且有序。`map` 的底层实现主要依赖于一种称为**红黑树Red-Black Tree)** 的自平衡二叉搜索树数据结构。下面,我将逐步解释其原理,确保内容清晰可靠,并基于提供的引用信息进行阐述。 #### 1. **底层数据结构:红黑树** - `map` 在底层使用红黑树实现,这是一种高度优化的二叉树,能够自动保持平衡,确保插入、删除和搜索操作的时间复杂度为 $O(\log n)$[^3]。 - **红黑树的性质**: - 每个节点都有一个颜色属性(红色或黑色)。 - 根节点始终是黑色的。 - 所有叶子节点(NIL节点)都是黑色的。 - 如果一个节点是红色的,则其子节点必须是黑色的(不能有两个连续的红色节点)。 - 从任意节点到其所有叶子节点的路径,必须包含相同数量的黑色节点(保持树平衡)。 - **节点结构**:每个节点存储一个键值对(key-value)。在 C++ 的实现中,节点通常定义为: ```cpp struct Node { Color color; // 节点颜色(红或黑) key_type key; value_type value; Node* left; Node* right; Node* parent; }; ``` 这个结构允许树通过指针调整来维护顺序和平衡[^3]。 #### 2. **实现机制详解** - **插入操作**: - 先像二叉搜索树一样插入新节点(新节点默认为红色),然后通过**旋转(rotation)和颜色变更(recoloring)** 来恢复红黑树性质。旋转包括左旋和右旋,主要用于调整节点位置而不破坏二叉排序树的特性。 - 插入后的调整最多需要 $O(1)$ 次旋转操作,整体时间复杂度为 $O(\log n)$,因为树的高度总是对数级。 - 示例:如果插入导致连续红色节点,会触发颜色变更或旋转来修复平衡[^3]。 - **删除操作**: - 删除键后,可能需要复杂的调整(如兄弟节点检查和旋转)来保持树平衡。删除操作的平均和最坏情况时间复杂度也为 $O(\log n)$。 - 删除时,如果移除黑色节点,会通过“双黑”机制或旋转来恢复平衡。 - **搜索操作**: - 基于二叉搜索树特性:key 小于当前节点时搜索左子树,大于时搜索右子树。由于红黑树的平衡性,树的高度近似 $O(\log n)$,因此搜索时间复杂度为 $O(\log n)$。数学公式表示为: $$ \text{高度 } h \leq 2 \log_2 (n+1) $$ 这确保搜索操作高效稳定[^3]。 #### 3. **优缺点分析** - **优点**: - 有序存储:键值对默认按 key 升序排列(可通过自定义比较器修改),适用于需要有序访问的场景。 - 高效操作:所有核心操作(插入、删除、搜索)都是 $O(\log n)$ 时间复杂度,适合处理动态数据集。 - 稳定性:红黑树的自平衡特性避免了二叉搜索树在极端数据下的退化(如树高度退化为 O(n),导致操作时间升为 O(n))。 - **缺点**: - 不支持随机访问:不同于 `vector` 或 `deque`,`map` 只能通过迭代器或 key 进行顺序或二分查找访问,不能直接下标访问(除非使用 `operator[]`,但内部本质仍是树搜索)。 - 内存开销:每个节点包含多个指针(left、right、parent 和 color 标志),空间复杂度为 O(n),但比基于哈希的容器(如 `unordered_map`)可能占用更多内存。 - 低局部性:节点非连续存储,缓存不友好,可能导致访问延迟(相比之下,`vector` 等连续存储容器有更好的局部性)[^4]。 #### 4. **应用场景** - `map` 适用于需要快速查找、插入和删除键值对的场景,例如数据字典、配置管理或缓存系统。其有序特性使其在范围查询(如查找 key 在 [a,b] 区间内的值)中特别高效。 - 与 `unordered_map` 对比:如果不需要有序性,`unordered_map`(基于哈希表实现)可能提供更快的平均 O(1) 搜索,但哈希冲突可能导致最坏情况 O(n) 的时间复杂度,而 `map` 的平衡树保证所有操作稳定在 O(log n)[^3]。 上述实现细节基于标准 C++ STL 库的设计(如 GCC 的 libstdc++ 或 Clang 的 libc++),实际实现可能略有优化,但核心原则一致[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值