STL学习记录(2) - 关联式容器

0. 序

学习STL源码做的笔记,主线是侯捷老师的《STL源码剖析》。
本文中部分图片来自侯捷老师的PPT。

1. 红黑树 - 数据结构

红黑树(RB-Tree):是一种平衡的二叉搜索树。有利于search和insert

红黑树提供“迭代器”和“遍历”操作。
顺序就是中序遍历的顺序。

注:虽然提供了迭代器,但是我们不应该通过迭代器修改元素的值。

STL中的红黑树提供了两种insert操作:inser_unique和insert_equal,前者表示节点的key在整棵树上独一无二,不能重复,否则插入失败,后者表示节点的key可以重复。

int main()
{
	// GNU 4.9之后的红黑树为_Rb_tree
    _Rb_tree<int, int, _Identity<int>, less<int>> itree;

    cout << itree.size() << endl;  // 0
    itree._M_insert_unique(3);
    itree._M_insert_unique(8);
    itree._M_insert_unique(5);
    itree._M_insert_unique(9);
    itree._M_insert_unique(13);
    itree._M_insert_unique(5);   // 这句是无效的插入,因为使用的插入方式是unique

    cout << itree.empty() << endl;  // 0
    cout << itree.size() << endl;  // 5
    cout << itree.count(5) << endl;  // 1

    itree._M_insert_equal(5);
    itree._M_insert_equal(5);

    cout << itree.size() << endl;  // 7
    cout << itree.count(5) << endl;  // 3


    return 0;
}

2. set与multiset

在这里插入图片描述

set与multiset是以红黑树为底层结构的,在使用红黑树时,它的key和value是同一个东西(没有value中的data部分),红黑树在初始化时,给keyOfValue参数使用了_Identity

因为set与multiset使用的是红黑树,所以它也有迭代器,但是用户无法通过迭代器修改元素,原因在于:它内部红黑树的迭代器被const修饰了。GNU 4.9的源代码如下:
在这里插入图片描述

  • set元素不能重复,因为它红黑树的插入方式使用的是insert_unique
  • multiset元素不能重复,因为它红黑树的插入方式使用的是insert_equal

3. map与multimap

在这里插入图片描述

与上边的set、multiset类似。

map与multimap是以红黑树为底层结构的,在使用红黑树时,但,它的key和value不是同一个东西(红黑树中的value是一个整体,包括key和data,data就是“键-值”中的值)。

因为map与multimap使用的是红黑树,所以它也有迭代器,我们无法通过它的迭代器修改key,但是可以修改data,原因在于:用户创建map类对象时,传入的是key和data的数据类型,而map和multimap类在构建内部的红黑树时,先将key和data打包成一个pair打包的时候,对key加了const限制,而data没有任何限制。所以,尽管它没有对自己内部红黑树的迭代器进行限制,但是达到了目的,技巧在于此。

但是它的迭代器被const修饰了,无法通过迭代器修改元素。GNU 4.9的源代码如下:
在这里插入图片描述

  • map元素不能重复,因为它红黑树的插入方式使用的是insert_unique
  • multimap元素不能重复,因为它红黑树的插入方式使用的是insert_equal

map可以使用中括号[]进行将指定的key所对应的data取出来,如果这个key不存在,那么map会直接创建这个key!这是标准中的规定。,源码如下:
在这里插入图片描述
先用lower_bound去找key出现的第一个位置,然后判断存不存在,如果不存在的话,就insert。可见,通过中括号的访问方式,因为要先进行一个lower_bound,所以要稍微耗时一点。

对set与map的小结

set与map(包括各自的multi版本)调用自己内部红黑树的关键区别在于:

  • set中的红黑树key就是value,而map中value包含key
  • set中不能通过迭代器修改元素,是对红黑树迭代器进行了const;而map中是在构建红黑树所需的value时,对key进行了const限制。

set与multiset的关键区别在于:

  • 内部红黑树的插入方式不同,普通版本使用的是insert_unique,而multi版本使用的是insert_equal

map与multimap的关键区别在于:

  • 内部红黑树的插入方式不同,普通版本使用的是insert_unique,而multi版本使用的是insert_equal
  • map可以使用中括号[ ]直接进行查询插入操作,但是,通过中括号的访问方式,因为要执行一个lower_bound操作,所以时间上不占优势。

4. hashtable-数据结构

hashtable(哈希表) 也叫 散列表、哈希表。

哈希表,最开始的想法是:
开 N 个bucket(篮子),每个篮子下挂一个链表,要哈希的每个元素都对N取余为 r,那么该元素就挂在下标是 r 的篮子下的链表上。可见,这种方式当碰撞较多的时候(碰撞:多个元素对N取余结果相同),链表的查找速度会很慢。

哈希表,改进后的想法:
先开 N 个bucket(篮子),如果当前已经被哈希的元素比 N 大了(经验上是觉得这个时候可能某个篮子下的链表就已经过长了),那么就扩充篮子的数量,扩充方式为:在 2*N 附近找一个质数作为新的篮子数量,然后对所有已经被哈希的元素重新进行放置(rehashing),这样以来,每个篮子下的链表都会变短,便于查找。

注意,尽管各家实现的哈希表可能不同,但是他们的bucket数量一定都比元素多,差别可能就是在篮子扩充时不太一样。

上述过程见下边图片 :
在这里插入图片描述
哈希表的迭代器有点类似deque的迭代器,需要能够进行跳跃,对外伪装成连续。

每个对象,如何得到它在哈希表中对应的数字(hashCode)呢?方法不一,只要不重复即可,STL中也对一些基础的数据类型做了hashCode方法,比如对于数字(int,long,char等)就直接让它本身作为hashCode,对于C中的字符串也有相应的方法(比如,hash<const char*>、hash<char *>)。

但是,ST中没有提供针对c++中string的hashCode方法(也就是说,没有hash<std::string>)。

5. unordered系列容器

在c++11以后,底层使用哈希表(hashTable)实现的容器有4个:

  • unordered_set
  • unordered_multiset
  • unordered_map
  • unordered_multimap
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值