C++ STL 容器:基于红黑树模拟实现 map 与 set
前言
在深入理解 STL 容器的底层之前,我们需要先掌握红黑树的基本概念与模拟实现。本文将在红黑树的基础上,逐步封装出符合 STL 规范的 map 和 set 容器。
一、STL 中 map/set 的底层结构
STL 中的 set 和 map 均是基于红黑树(Red-Black Tree)封装实现的。观察其源码可以发现一些设计细节:
- set 的实现:
set内部封装了一个rb_tree对象,传入的模板参数为key_type和value_type,两者类型均为Key。这看似多余,实则是为了泛型设计的统一性。 - map 的实现:
map同样封装了rb_tree,但value_type是pair<const Key, T>。这里存储的是键值对,而不仅仅是 value。 - rb_tree 的设计:红黑树的节点定义使用了第二个模板参数
Value来存储实际内容。对于map,传入的是pair<Key, Value>;对于set,传入的是Key。 - 为何需要第一个模板参数 Key:这是为了支持
find、erase等接口。这些操作需要基于 Key 进行查找或删除。如果只存储Value,map就无法直接通过 Key 定位,因此必须显式传入Key类型作为辅助信息。
这种设计使得 map 和 set 可以共用同一套红黑树类模板,只需传入不同的模板参数即可实例化出不同的行为。
二、红黑树迭代器的实现
为了实现遍历功能,我们需要模拟实现红黑树的迭代器。迭代器本质上是对节点指针的封装,模拟指针的行为。
1. 节点结构优化
原红黑树设计为 key_value 结构,为了适配 map 和 set,我们将模板参数调整为 <K, T>,其中 T 代表节点中实际存储的类型(可能是 Key 或 pair<K, V>)。
template<typename T>
struct RBTreeNode {
T _data;
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
// 构造函数略
};
2. 迭代器重载
迭代器需要重载 *, ->, !=, ++, -- 等操作符。重点在于 operator++ 和 operator-- 的逻辑,它们决定了中序遍历的顺序。
- operator++: 若右子树存在,则找右子树的最左节点;否则向上寻找父节点,直到找到一个是其父节点左孩子的节点。


