
目录
[1 SGI‑STL 源码框架分析](#1 SGI‑STL 源码框架分析)
[1.1 源码模板拆解](#1.1 源码模板拆解)
[set 源码片段](#set 源码片段)
[map 源码片段](#map 源码片段)
[rb_tree 红黑树模板](#rb_tree 红黑树模板)
[2 模拟实现框架设计](#2 模拟实现框架设计)
[2.1 KeyOfT 仿函数](#2.1 KeyOfT 仿函数)
[2.2 分层封装](#2.2 分层封装)
[3 红黑树迭代器实现(难点)](#3 红黑树迭代器实现(难点))
[3.1 迭代器模板](#3.1 迭代器模板)
[RBTree 内部定义两套迭代器](#RBTree 内部定义两套迭代器)
[4 RBTree 的 Insert 接口改造](#4 RBTree 的 Insert 接口改造)
[5 封装 bit::set](#5 封装 bit::set)
[6 封装 bit::map,重点 operator \[\]](#6 封装 bit::map,重点 operator [])
本节学习目标
- 看懂 SGI‑STL 中
map/set底层复用同一套rb_tree红黑树的源码框架;理解为什么一颗红黑树可以同时支持 set(纯 key)和 map(key‑value)。 - 掌握KeyOfT 仿函数核心设计:红黑树结点存的数据类型 T 不确定,通过仿函数从 T 里面提取 key 做比较。
- 手写红黑树迭代器:重点实现
++、--,基于红黑树中序遍历 规则;理解begin()、end()。 - 区分普通迭代器、const 迭代器;理解
map的pair<const K,V>,key 不能修改,value 可以修改;set 全部元素不可修改。 - 实现
map::operator[],依赖 Insert 返回pair<iterator,bool>。 - 完整实现
bit::set、bit::map封装类。
1 SGI‑STL 源码框架分析
STL 的
set、map并不是各自独立实现一棵树 ,二者复用同一套rb_tree红黑树。
1.1 源码模板拆解
set 源码片段
cpp
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:
typedef Key key_type;
typedef Key value_type; // set:key与value_type完全一样
private:
// identity<value_type>:仿函数,直接把key本身返回
typedef rb_tree<key_type, value_type, identity<value_type>, key_compare, Alloc> rep_type;
rep_type t;
};
- set 结点存的是
Key,value_type=Key。 identity仿函数:输入 T,直接返回 T 本身,用于提取 key。
map 源码片段
cpp
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:
typedef Key key_type;
typedef T mapped_type;
typedef pair<const Key, T> value_type; // map结点存pair<const Key,T>
private:
// select1st<value_type>仿函数:取出pair的first作为key
typedef rb_tree<key_type, value_type, select1st<value_type>, key_compare, Alloc> rep_type;
rep_type t;
};
- map 结点存储
pair<const Key,T>;pair 的 first 是 const K,禁止修改 key,防止破坏红黑树有序性。 select1st仿函数:接收 pair 对象,返回pair.first,拿到 key。
rb_tree 红黑树模板
cpp
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
class rb_tree
{
protected:
typedef __rb_tree_node<Value> rb_tree_node;
public:
pair<iterator,bool> insert_unique(const value_type& x);
iterator find(const key_type& x);
private:
link_type header; // 哨兵头结点(STL源码特有)
};
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
Value value_field; // 结点真正存储的数据
};
✨核心设计思想(面试重点)
- 红黑树第二个模板参数
Value:决定结点里面存什么 。- set 实例化 rb_tree:
Value = Key,结点存普通 key; - map 实例化 rb_tree:
Value = pair<const K,V>,结点存键值对。
- set 实例化 rb_tree:
KeyOfValue是仿函数:统一从结点存储的 Value 对象中提取出用于比较的 key 。- set:传入
identity,输入 key 返回 key; - map:传入
select1st,输入 pair 返回 pair.first。
- set:传入
- rb_tree 第一个模板参数
Key:给find、erase使用,find 传参只需要 key,不需要传完整 value 对象。

吐槽:STL 源码命名混乱,rb_tree 的
Value不是 map 里面的 mapped‑value,是结点完整存储的数据 value_type。
STL 源码 rb_tree 有header哨兵头结点,begin 指向 header 的 left(树最小结点),end 指向 header;我们手写简化版本直接用nullptr代表 end。
2 模拟实现框架设计
2.1 KeyOfT 仿函数
问题:红黑树模板类
RBTree<K,T,KeyOfT>,T 不确定:
- set:T 就是 K;
- map:T 是
pair<const K,V>。 做插入、查找比较时,不能直接拿 T 比较,要提取出 key,仿函数干提取 key 这件事。
cpp
// set用的仿函数:直接返回key本身
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
// map用的仿函数:提取pair的first(key)
struct MapKeyOfT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
pair 原生
<运算符:先比较 first,如果 first 相等才比较 second。 如果直接拿 pair 做整对象比较,会把 value 也参与比较;红黑树只允许拿 key 做比较,所以必须 KeyOfT 只拿 key 比较。
2.2 分层封装
RBTree<K,T,KeyOfT>底层红黑树模板,通用,同时给 map 和 set 复用。bit::set<K>:内部私有成员RBTree<K,const K,SetKeyOfT> _t;
set 的 T 用
const K,迭代器解引用是 const,禁止修改元素,防止破坏树。
bit::map<K,V>:内部私有成员RBTree<K,pair<const K,V>,MapKeyOfT> _t;
pair 的 first 是 const K,key 不可修改,second (V) 可以修改。
红黑树结点定义
cpp
enum Colour
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
T _data;
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
RBTreeNode(const T& data)
:_data(data)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_col(RED)
{}
};
3 红黑树迭代器实现(难点)
set/map 迭代器本质是中序遍历迭代器 ,
++走到中序下一个结点,--走到中序上一个结点。 底层不是链表,结点左右孩子指针,封装迭代器类,内部保存结点指针,重载++ -- * -> == !=。
3.1 迭代器模板
cpp
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef RBTreeIterator<T, Ref, Ptr> Self;
Node* _node;
Node* _root;
RBTreeIterator(Node* node, Node* root)
:_node(node)
,_root(root)
{}
// ++前置,中序找下一个结点
Self& operator++()
{
if (_node->_right != nullptr)
{
//右子树不为空:下一个 = 右子树的最左结点
Node* leftMost = _node->_right;
while (leftMost->_left)
{
leftMost = leftMost->_left;
}
_node = leftMost;
}
else
{
//右为空,向上找祖先:找到第一个孩子是父亲左的祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
// --前置,中序找前一个结点
Self& operator--()
{
if (_node == nullptr)
{
// --end()特殊处理:end是nullptr,要跳转到整棵树最右结点
Node* rightMost = _root;
while (rightMost && rightMost->_right)
{
rightMost = rightMost->_right;
}
_node = rightMost;
}
else if (_node->_left != nullptr)
{
//左不为空:下一个是左子树的最右结点
Node* rightMost = _node->_left;
while (rightMost->_right)
{
rightMost = rightMost->_right;
}
_node = rightMost;
}
else
{
//左为空,向上找祖先,找到第一个孩子是父亲右的祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
Ref operator*() const
{
return _node->_data;
}
Ptr operator->() const
{
return &_node->_data;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
迭代器规则梳理
begin():中序第一个结点,整棵树最左结点。end():简化实现用nullptr;STL 源码是哨兵 header 结点。++逻辑- 如果当前结点右孩子不为空:下一个结点是右子树的最左结点;
- 如果右孩子为空:向上循环找祖先;当
cur != parent->_right停止,parent 就是下一个结点;找到根 parent=nullptr,变成 end。
--逻辑- 如果迭代器是
end()(_node=nullptr):特殊处理,直接跳转到整棵树最右结点; - 如果左孩子不为空:下一个是左子树最右结点;
- 左孩子为空:向上找祖先,找到第一个
cur != parent->_left的祖先。
- 如果迭代器是

RBTree 内部定义两套迭代器
cpp
template<class K, class T, class KeyOfT>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
//普通迭代器 Ref=T&, Ptr=T*
typedef RBTreeIterator<T, T&, T*> Iterator;
//const迭代器 Ref=const T&, Ptr=const T*
typedef RBTreeIterator<T, const T&, const T*> ConstIterator;
Iterator Begin()
{
Node* leftMost = _root;
while(leftMost && leftMost->_left)
leftMost = leftMost->_left;
return Iterator(leftMost, _root);
}
Iterator End()
{
return Iterator(nullptr, _root);
}
ConstIterator Begin() const
{
Node* leftMost = _root;
while(leftMost && leftMost->_left)
leftMost = leftMost->_left;
return ConstIterator(leftMost, _root);
}
ConstIterator End() const
{
return ConstIterator(nullptr, _root);
}
//......
};
关键点:
- set 底层存
const K,所以不管普通迭代器还是 const 迭代器,解引用都是 const 引用,set 元素禁止修改。- map 底层存
pair<const K,V>,first是 const 不可修改,second可以修改。
4 RBTree 的 Insert 接口改造
原来的 Insert 返回 bool;现在必须返回pair<Iterator,bool> ,给 map 的operator[]使用。
返回值含义:
.first:插入元素的迭代器;如果 key 重复,返回已经存在结点的迭代器。.second:bool,true 插入成功;false 代表 key 已经存在,插入失败。
cpp
pair<Iterator, bool> Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(Iterator(_root,_root), true);
}
KeyOfT kot;
Node* parent = nullptr;
Node* cur = _root;
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
{
//key重复,插入失败,返回已存在结点迭代器
return make_pair(Iterator(cur,_root), false);
}
}
cur = new Node(data);
Node* newnode = cur;
cur->_col = RED;
if (kot(parent->_data) < kot(data))
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
//--------红黑树向上更新平衡、旋转(此处省略,和红黑树课件逻辑完全一样)--------
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if(parent == grandfather->_left)
{
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(cur == parent->_right)
{
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,_root), true);
}
Find 接口:传入 key,返回 Iterator,找不到返回 End ()。
cpp
Iterator Find(const K& key)
{
KeyOfT kot;
Node* cur = _root;
while(cur)
{
if(kot(cur->_data) < key)
cur = cur->_right;
else if(kot(cur->_data) > key)
cur = cur->_left;
else
return Iterator(cur,_root);
}
return End();
}
5 封装 bit::set
cpp
namespace bit
{
template<class K>
class set
{
private:
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
//底层T使用const K,禁止修改元素
typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator 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 K& key)
{
return _t.Insert(key);
}
iterator find(const K& key)
{
return _t.Find(key);
}
private:
RBTree<K, const K, SetKeyOfT> _t;
};
}
set 注意点:
- 底层
const K,迭代器解引用是 const,不能修改集合里面的元素;- 不支持
operator[],set 没有这个接口。
6 封装 bit::map,重点 operator \[\]
cpp
namespace bit
{
template<class K, class V>
class map
{
private:
struct MapKeyOfT
{
const K& operator()(const pair<const 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>::ConstIterator 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<const K, V>& kv)
{
return _t.Insert(kv);
}
iterator find(const K& key)
{
return _t.Find(key);
}
//⭐operator[]核心实现
V& operator[](const K& key)
{
//如果key不存在,插入key+V()默认对象;存在则拿到已有迭代器
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
}
operator[]三合一功能回顾:
- key 不存在:插入
pair(key,V()),返回 value 引用;- key 已经存在:不插入,直接返回已有 value 引用;
- 返回引用,支持读取、修改 value。
map 重要限制:pair<const K,V>,不能修改 it->first(key),只能修改 it->second。
📔本篇核心考点总结(面试必背)
- STL 中
map和set底层复用同一套红黑树 rb_tree:- set 结点存
Key;map 结点存pair<const Key,T>; - 通过
KeyOfT仿函数从存储的数据对象提取 key,做比较。
- set 结点存
- KeyOfT 仿函数:解决 T 类型不确定,统一提取 key,不能拿 pair 整体做比较。
- 红黑树迭代器本质中序遍历迭代器 ;
++、--不需要递归,靠结点 parent 指针向上回溯;end()简化版本用 nullptr,--end()特殊处理跳转到最右结点。 - set 底层存储
const K,全部元素禁止修改;map 中pair<const K,V>,key 不可修改,value 可以修改。 - RBTree 的 Insert 返回
pair<iterator,bool>:迭代器 + 是否插入成功,这是实现 mapoperator[]的基础。 map::operator[]:调用 insert,key 不存在插入默认构造 value,返回 value 的引用;不要用 \[\] 单纯做查找,不存在会插入垃圾数据。
面试简答
Q:为什么 set/map 的迭代器不能修改 set 的 key,map 不能修改 pair 的 first? A:底层是红黑树,key 是维持红黑树有序平衡的依据,如果修改 key,直接破坏红黑树搜索树性质,造成逻辑错误。所以 set 的 T 是 const K;map 结点存
pair<const K,V>,first 被 const 修饰。
Q:map 的 operator \[\] 底层原理? A:调用 insert,传入make_pair(key,V());key 不存在执行插入;key 存在直接拿到原有结点迭代器;返回iterator->second即 value 的引用,实现读、写。
Q:红黑树迭代器 ++ 是怎么实现,是递归中序遍历吗? A:不是递归,利用 parent 父指针向上回溯。右子树不为空找右子树最左;右子树空向上找祖先,找到第一个孩子是父亲左的祖先,就是中序下一个结点。
拓展:multiset/multimap 底层 rb_tree 使用insert_equal,允许 key 重复;我们当前实现是insert_unique,key 唯一,对应 set/map。


