std::set/std::map简介2014-04-03std::set/std::map (以下用 std::map 代表) 是常用的关联式容器,也是 ADT(抽象数据类型) 。也就是说,其接口(不是 OO 意义下的 interface)不仅规定了操作的功能,还规定了操作的复杂度 (代价/cost)。例如 set::insert(iterator first, iterator last) 在通常情况下是 O(N log N), N 是区间的长度;但是如果 [first, last) 已经排好序(在 key_compare 意义下),那么复杂度将会 是 O(N)。尽管 C++ 标准没有强求 std::map 底层的数据结构,但是根据其规定的时间复杂度 ,现在所有的 STL 实现都采用平衡二叉树来实现 std::map,而且用的都是红黑树。《算法导论(第 2 版)》第 12、13 章介绍了二叉搜索树和红黑树的原理、性质、伪代码,侯捷先生的《STL 源码剖析》 第 5 章详细剖析了 SGI STL 的对应实现。本文对 STL 中红黑树(rb_tree)的实现问了几个稍微深入 一点的问题,并给出了我的理解。本文剖析的是 G++ 4.7 自带的这一份 STL 实现及其特定行为,与《 STL 源码剖析》的版本略有区别。为了便于阅读,文中的变量名和 class 名都略有改写(例如 _Rb_tree_node 改为 rb_tree_node)。本文不谈红黑树的平衡算法,在我看来这属于“旁枝末节”( 见陈硕《谈一谈网络编程学习经验》对此的定义),因此也就不关心节点的具体颜色了。数据 结构回顾先回顾一下数据结构教材上讲的二叉搜索树的结构,节点(Node)一般有三个数据成 员(left、right、data),树(Tree)有一到两个成员(root 和 node_count)。
用 Python 表示:class Node:def __init__(self, data):self.left = Noneself.right = Noneself.data = data class Tree:def __init__(self):self.root = Noneself.node_count = 0
而实际上 STL rb_tree 的结构比这个要略微复杂一些,我整理的代码见 https://gist.github.com/4574621#file-tree-structure-cc 。节点Node 有 5 个成 员,除了 left、right、data,还有 color 和 parent。
C++实现,位于bits/stl_tree.h/** * Non-template code **/ enum rb_tree_color { kRed, kBlack }; struct rb_tree_node_base{rb_tree_color color_;rb_tree_node_base*parent_;rb_tree_node_base*left_;rb_tree_node_base*right_;}; /** * template code **/ template<typename Value>struct rb_tree_node : public rb_tree_node_base{Value value_field_;};
见下图。

color 的存在很好理解 ,红黑树每个节点非红即黑,需要保存其颜色(颜色只需要 1-bit 数据,一种节省内存的优化措施是 把颜色嵌入到某个指针的最高位或最低位,Linux 内核里的 rbtree 是嵌入到 parent 的最低位); parent 的存在使得非递归遍历成为可能,后面还将再谈到这一点。