SGI STL中set/map底层都是通过RB-tree实现的。

首先看看RB-tree结点的定义

 typedef bool __rb_tree_color_type;
const __rb_tree_color_type __rb_tree_red = false;
const __rb_tree_color_type __rb_tree_black = true; // 结点的基类
struct __rb_tree_node_base
{
typedef __rb_tree_color_type color_type;
typedef __rb_tree_node_base* base_ptr; // 关键的4个域
color_type color;
base_ptr parent;
base_ptr left;
base_ptr right; // 返回极值
static base_ptr minimum(base_ptr x)
{
while (x->left != ) x = x->left;
return x;
} static base_ptr maximum(base_ptr x)
{
while (x->right != ) x = x->right;
return x;
}
} // 多了一个value域
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
typedef __rb_tree_node<Value>* link_type;
Value value_field;
};

下图是RB-tree结点跟其迭代器的关系

重点看看__rb_tree_iterator的operator++跟operator--,

实际是调用__rb_tree_base_iterator的increment跟decrement。

可以看出迭代器前移/后移的时候会按key的顺序找到下一个/上一个结点。

(set/map<...>::begin()会返回RB-tree中key最小的结点,因此使用operator++遍历会按key的顺序从小到大遍历结点)

 void increment()
{
if (node->right != ) {
node = node->right;
while (node->left != )
node = node->left;
}
else {
base_ptr y = node->parent;
while (node == y->right) {
node = y;
y = y->parent;
}
if (node->right != y)
node = y;
}
} void decrement()
{
if (node->color == __rb_tree_red &&
node->parent->parent == node)
node = node->right;
else if (node->left != ) {
base_ptr y = node->left;
while (y->right != )
y = y->right;
node = y;
}
else {
base_ptr y = node->parent;
while (node == y->left) {
node = y;
y = y->parent;
}
node = y;
}
}

SGI STL中的rb_tree用了一个小trick,就是使用了一个header结点,用来代表整个rb_tree。

该结点与root结点互为父结点,该结点的left指向最左(key最小)的结点,right指向最右(key最大)的结点。

除此之外,该rb_tree的实现跟普通的红黑树类似,详情可以查看:http://www.cnblogs.com/runnyu/p/4679279.html。

 template <class Key, class Value, class KeyOfValue, class Compare,
class Alloc = alloc>
class rb_tree {
// rb_tree的基本定义
protected:
typedef __rb_tree_node_base* base_ptr;
typedef __rb_tree_node<Value> rb_tree_node;
typedef simple_alloc<rb_tree_node, Alloc> rb_tree_node_allocator;
public:
typedef Key key_type;
typedef Value value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef rb_tree_node* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type; typedef __rb_tree_iterator<value_type, reference, pointer> iterator; link_type header;
// ... // 主要接口
iterator begin() { return leftmost(); } // 返回最左边的结点(最小key)
iterator end() { return header; } iterator insert_equal(const value_type& x); // 插入元素 并允许键值相同
pair<iterator,bool> insert_unique(const value_type& x); // 插入元素 键值是独一无二的 };

set/multiset

有了rb_tree,set/multiset的实现也只是调用rb_tree的接口而已。

其中set跟multiset不一样的是,set插入的时候调用的是insert_unique(),而multiset调用的是insert_equal()。

下面是给出set的基本定义:

 template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:
typedef Key key_type;
typedef Key value_type; // 使用的value类型跟key一样
typedef Compare key_compare;
typedef Compare value_compare;
private:
typedef rb_tree<key_type, value_type,
identity<value_type>, key_compare, Alloc> rep_type;
rep_type t;
public:
// 接口的实现只是对rb_tree的封装 不一一列举了
iterator begin() const { return t.begin(); }
iterator end() const { return t.end(); }
pair<iterator,bool> insert(const value_type& x) {
pair<typename rep_type::iterator, bool> p = t.insert_unique(x);
return pair<iterator, bool>(p.first, p.second);
}
// ...
};

map/multimap

map/mulitmap的实现也是通过调用rb_tree的接口。

map/mulitmap不一样的是,map插入的时候调用的是insert_unique(),而multimap调用的是insert_equal()。

下面是给出map的基本定义:

 template <class Key, class T, class Compare = less<Key>, class Alloc
class map {
public:
typedef Key key_type;
typedef T data_type;
typedef T mapped_type;
typedef pair<const Key, T> value_type; // 在rb_tree中value的类型是pair
typedef Compare key_compare;
private:
// select1st直接return T.first 用于rb_tree取到key进行比较大小
typedef rb_tree<key_type, value_type,
select1st<value_type>, key_compare, Alloc> rep_type;
rep_type t;
// ... // 接口只是对rb_tree的封装 就不一一列举了
iterator begin() { return t.begin(); }
iterator end() { return t.end(); }
pair<iterator,bool> insert(const value_type& x) { return t.insert_unique(x); }
// ...
}

另外STL中有未列入标准的hash_set/hash_map以及C++11中的unordered_set/map,底层是使用hashtable实现的。

相比于用rb_tree实现的set/map,它们的插入删除查找操作具有O(1)的时间复杂度(没有冲突情况下),但是它们的元素的顺序是无序的。

STL源码剖析(set/map)的更多相关文章

  1. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

  2. 【转载】STL"源码"剖析-重点知识总结

    原文:STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点 ...

  3. STL源码剖析 迭代器(iterator)概念与编程技法(三)

    1 STL迭代器原理 1.1  迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...

  4. STL"源码"剖析

    STL"源码"剖析-重点知识总结   STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略 ...

  5. 《STL源码剖析》相关面试题总结

    原文链接:http://www.cnblogs.com/raichen/p/5817158.html 一.STL简介 STL提供六大组件,彼此可以组合套用: 容器容器就是各种数据结构,我就不多说,看看 ...

  6. 《STL源码剖析》读书笔记

    转载:https://www.cnblogs.com/xiaoyi115/p/3721922.html 直接逼入正题. Standard Template Library简称STL.STL可分为容器( ...

  7. STL源码剖析之组件

    本篇文章开始,进行STL源码剖析的一些知识点,后续系列笔记全是参照<STL源码剖析>进行学习记录的 STL在现在的大部分项目中,实用性已经没有Boost库好了,毕竟STL中仅仅提供了一些容 ...

  8. 面试题总结(三)、《STL源码剖析》相关面试题总结

    声明:本文主要探讨与STL实现相关的面试题,主要参考侯捷的<STL源码剖析>,每一个知识点讨论力求简洁,便于记忆,但讨论深度有限,如要深入研究可点击参考链接,希望对正在找工作的同学有点帮助 ...

  9. 通读《STL源码剖析》之后的一点读书笔记

    直接逼入正题. Standard Template Library简称STL.STL可分为容器(containers).迭代器(iterators).空间配置器(allocator).配接器(adap ...

随机推荐

  1. 【BZOJ 1095】 1095: [ZJOI2007]Hide 捉迷藏 (括号序列+线段树)

    1095: [ZJOI2007]Hide 捉迷藏 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏 ...

  2. hdu 5868 2016 ACM/ICPC Asia Regional Dalian Online 1001 (burnside引理 polya定理)

    Different Circle Permutation Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 262144/262144 K ...

  3. 【计算几何】【圆反演】计蒜客17314 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛 G. Finding the Radius for an Inserted Circle

    题意:给你三个半径相同的圆,它们切在一起,然后让你往缝里一个一个地塞圆,问你塞到第k个的半径是多少. 就把上面那两个圆的切点当成反演中心,然后会反演成这个样子,两个平行直线和一个圆. 然后就是往那个圆 ...

  4. bzoj 4506: [Usaco2016 Jan]Fort Moo

    4506: [Usaco2016 Jan]Fort Moo Description Bessie is building a fort with her friend Elsie. Like any ...

  5. Java并发(三):重排序

    在执行程序时为了提高性能,提高并行度,编译器和处理器常常会对指令做重排序.重排序分三种类型: 编译器优化的重排序.编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序. 指令级并行的重排序 ...

  6. [NOIP2013 花匠] 新人解题报告

    本来按照老师的要求,我学OI的第一份解题报告应是在寒假完成的关于数据结构的基础题,但由于身体原因当时未能完成,那么就在省选赛前临时写几篇吧…… 题目描述 花匠栋栋种了一排花,每株花都有自己的高度.花儿 ...

  7. Android内存优化4 了解java GC 垃圾回收机制2 GC执行finalize的过程

    1. finalize的作用 finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法. finalize()与C++中的析构函数 ...

  8. linux下编译运行C程序

    GCC是Linux操作系统下一个非常重要的源代码编译工具,有着许多重要的选项,支持许多不同语言的编译,如C.C++.Ada.Fortran.Objective.Perl.Python.Ruby以及Ja ...

  9. Ubuntu - 硬盘分区、格式化、自动挂载配置

    Ubuntu系统的硬盘空间不够用了,需要增加新的硬盘扩容.将硬盘分区.格式化.自动挂载配置的整个过程记下来,备忘. 运行环境 | Enviroment Ubuntu 10.10 一.硬盘分区 | Ha ...

  10. MySql Replication基本原理

    Replication的思想是将数据在集群的多个节点同步.备份,以提高集群数据的可用性(HA):Mysql使用Replication架构来实现上述目的,同时可以提升了集群整体的并发能力.5.6版本作为 ...