关联容器
有序集合:
map、set、multiset以及multimap (其中multiXXX表示关键字可重复出现)
无序集合:
unordered_map、unordered_set、unordered_multiset以及unordered_multimap
二者区别在于,有序集合是使用红黑树来实现的,而无序集合是使用哈希表来实现的。
PS:有序容器是使用比较函数来比较关键字的,默认情况下,比较操作是采用关键字类型的<运算符。所以对于自建对象要想实现有序存放可以自己写<运算符的重载函数。
重点说说hashtable
哈希表的重点其实就是解决2个问题:
1. 设计一个有效的映射函数,将各种数据类型给映射成一个unsigned int 这种数组下标的形式
2. 解决碰撞问题。(经典方法有线性探测、二次探测,拉链法) 线性探测简单讲就是这个位置被占了,ok,那我就+1,去下一个位置看看。二次探测就是h这个位置被占了,那我去 h+1^2,再不行去h+2^2。。。。反正探测法其实就是用一个大数组来解决碰撞问题,每个坑只会对应一个元素(坑就是数组的每个元素),而拉链法其实就是我这个数组存的元素是一个链表的节点,每个坑会对应多个元素。(开放地址法和拉链法,谁的性能更优是看具体情况的。不过我觉得拉链法更好,因为拉链法容易被诟病的是因为它vector的元素是一个链表节点,所以除了val之外还额外需要一个指针的内存开销,而开放地址法的问题在于容易形成一个主簇,就是一大堆元素会集中在一起,开放地址法的删除一般是用惰性删除实现的,就是标记这个元素已经被删除,实际没有删除,好像可以union来实现,节省内存,ok前面的所有当我没说)
stl里的哈希table实现就是采用拉链法,并且桶的重组是当元素的个数超过桶的个数也就是vector的size,所以每条链表的最大长度跟vector的size是一样的,并且vector的size一般都是用一个质数(好像原因是 数mod一个质数,结果会均匀分配,我记得好像是这样)。
哈希table的insert是采用头插法。
hashtable的迭代器是forward_iterator
-
标准的 STL 关联式容器分为 set(集合) 和 map(映射表) 两大类。衍生的还有 multiset(多键集合) 和 multimap(多键映射表)。这些容器的底层机制都是 RB-tree(红黑树原理) 完成。
-
散列表 hash table(Hash Table 原理),以 hash table 为底层机制而完成的 hash_set、hash_map、hash_multiset、hash_multimap。
以 RB-tree 为底层机制的关联式容器
底层机制 RB-tree
SGI STL 有实现 RB-tree(红黑树) 的源码。
红黑树是一棵特殊的二叉搜索树。
二叉搜索树的元素插入操作:插入新元素时,可以从根节点开始,遇到键值比插入元素大就向左,遇到键值比插入元素小就向右,一直到尾端,即为插入点。
二叉搜索树的元素删除操作:删除节点 A,当 A 只有一个子节点,就直接将 A 的子节点连接到 A 的父节点,并将 A 删除。当 A 有两个子节点,将其右子树内的 最小节点取代 A。
set
set 元素的键值就是实值,实值就是键值。简单理解,含有 Key 类型对象的已排序集。
set 不允许两个元素有相同的键值,同时所有元素根据键值自动排序。
template< class Key, class Compare = std::less<Key>, // 默认递增排序 class Allocator = std::allocator<Key> > class set;
map
map 元素是键值对 pair,同时拥有键值(key)和实值(value)。
map 不允许元素的键值有相同,必须唯一。
map 所有元素按照元素的键值自动排序。
template< class Key, class T, class Compare = std::less<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > class map;
multiset
multiset 和 set 的用法和特性完全相同,唯一的差别是 multiset 允许键值重复。
multiset 的插入操作采用的是底层机制 RB-tree 的 insert_equal()。
头文件:#include <set>
multimap
multimap 和 map 的用法和特性完全相同,唯一的差别是 multimap 允许键值重复。
multimap 的插入操作采用的是底层机制 RB-tree 的 insert_equal()。
头文件:#include <map>
以 hash table 为底层机制的关联式容器
底层机制 hashtable
hash table(散列表)数据结构,在插入、删除、查找等操作具有 “常数平均时间” O(1) 的表现,这种表现是以统计为基础,不需要依赖输入元素的随机性。
当索引值非常庞大,造成需要很大的 array,这样需要使用某种映射函数,将大数映射为小数。负责将某一个元素映射为一个“大小可接受之索引”,这种映射函数称为散列函数(hash function)。
SGI STL 的 hash table 采用 separate chaining 链接法来处理碰撞问题。
散列表使用 vector,包含 N 个 bucket,每个 bucket 所维护的 linked list 是自构造的。
hash table 的迭代器没有后退操作,也没有逆向迭代器。
客户端程序不能直接包含 <stl_hashtable.h>, 而是包含有用到 hashtable 的容器头文件,如 <hash_set.h> 或 <hash_map.h>。
hash table 的 API
insert_unique(): 插入元素,不允许重复
insert_equal(): 插入元素,允许重复
find(): 查找元素
resize(): 调整表格
hashtable 大小指定为 50(根据 SGI 的设计,采用质数 53)。
hash_set
RB-tree 有自动排序功能而 hashtable 没有,所以 set 的元素有自动排序功能而 hash_set 没有。
hash_set 大小指定为 100(根据 SGI 的设计,采用质数 193)。
hash_map
运用 map 为的是能够根据键值快速查找元素。RB-tree 有自动排序功能而 hashtable 没有,所以 map 的元素有自动排序功能而 hash_map 没有。
hash_multiset
hash_multiset 与 hash_set 的真正差别是前者插入操作采用底层机制 hashtable 的 insert_equal(),后者采用 insert_unique()。
hash_multimap
hash_multimap 与 hash_map 的真正差别是前者插入操作采用底层机制 hashtable 的 insert_equal(),后者采用 insert_unique()。