C++ unordered Associative Containers(C++11)

C++11 引进了无序关联容器(unordered associative containers)的概念。 有unordered set or multiset, 以及unordered map or multimap。

顾名思义, unordered的意思就是元素没有固定的顺序, 并且元素的顺序可能会随着时间的变化而变化。

C++ unordered Associative Containers(C++11)

 

Internally, unordered container 是使用hash table(哈希表)实现的。 所谓的hash Table, 就是an array of linked list. 整个数组又被称为an array of buckets。

那些linked lsit 被称为entries。

每一个元素作为一个hash function的输入参数,  hash function 输出这个元素对应的hash value(哈希值)。 然后根据这个值将这个元素添加到对应的linked list 的合适的位置。

C++ unordered Associative Containers(C++11)

 

上述结构的优点就是, 当你有一个fast and effective function , finding an element in the hash table is very fast, 花费的时间是常数时间O(1). 这是所有的容器中最快的一种。  

 

例如下图, 创键了一个unordered set (无序集合)of string, 名字为myset(存储着red, green, blue);

然后使用set的成员函数find()去查找myset 中的green,  这一步仅仅需要花费常数时间(O(1))非常之快。 如果找到了这个元素, 该函数会返回指向这个元素的迭代器, 如果没有找到这个元素, 那么该函数会返回一个指向the end of the container 的迭代器, 所以我们需要Check 是否找到了。 所以如下验证。 如果返回的是一个未定义的, 说明指向容器末尾, 也就是没有找到;

然后我们想这个无序set 中插入一个yellow, 这个操作也仅仅需要花费O(1);

我们也可以实用另外一个容器作为insert 的参数, 例如向量容器vec, myset 调用insert成员函数的时候, 可以将这个容器的所有元素都插入到无序容器myset中, 非常有效。

 

无序集合也为我们提供了hash Table的API, 

load_factor() 是给出元素的总数与bucket的总数的比值。

函数bucket告诉我们我们的元素存储在那个bucket 之中。

bucket_count 告诉我们容器中有多少个bucket。

 

C++ unordered Associative Containers(C++11)

 

unordered multiset 允许我们的容器具有duplicated elements:

unodered map 非常想unordereded set, 区别就是unordered 的元素是有一组key 和value组成。

unordered multimap 允许duplicated keys。

我们需要记住的一点就是, 尽管hash Table 允许amortized constant time search, 但是这个性能会随着hash collision(哈希碰撞)而退化(降低)。

Q: 什么是hash collision:

A: 许多的元素被插入到同一个bucket中。

 

 

C++ unordered Associative Containers(C++11)

 

最坏的情况下, 当所有的元素被插入到一个bucket 的时候, 性能最差, 此时搜索所需要的时间变为了O(n):

C++ unordered Associative Containers(C++11)

 

 

无序容器的属性如下:

C++ unordered Associative Containers(C++11)

 

注意一点, 对于unordered set/multiset, 元素的值是不能够改变的, 因为改变值的时候, 将会改变其的data structures。也就是改变了hash table。

对于unordered map/multimap, 元素的key 也是不能随便改变的。 原因同上, 即会改变我们的hash table。

 

 

尽管STL中, 没有容器被称为关联数组(associative array), 但是我们仍然可以通过map 和unordered map 去实现它。

例如下面, 我们创建了一个unordereded map称为day, 存储的内容如下。

接下来, 存取相关的值就相当于存取数组的元素一样(通过索引, 只不过这里是key)。 注意用[] 存取无range Check, 用成员函数at 有range check。

 

需要注意的一点就是, 如果是向量, 如下, 使用vec[5]就会出错, 因为vec[5]并没有被创建(vec 只有三个元素)。

然而, 对于关联容器, 指令day[‘W ‘ ] = "Wednesday"会将{“W”, “Wednesday”}插入到关联容器中。 当然我们也可以调用insert函数。

然而, insert函数不能用于修改已经存在的元素值。 然而下标法却可以实现修改。 因为此时将其视为关联数组。

 

 

 

C++ unordered Associative Containers(C++11)

 

对于如下函数:

参数是pass by reference(const)。

在函数中, 我们修改了m, 所以下面的函数也不会通过编译。 因为m is not modifiable。

 

 

C++ unordered Associative Containers(C++11)

 

在看下面的函数, 假如我们只是打印出来, 那么会打印出来吗?

正确的答案是下面也不会通过编译, 出现的错误信息和上面的出现的错误信息是一样的。  原因是什么呢?

因为下标法[] 对我们的容器的操作是写操作。 所以当编译器看到m[‘S‘], 会assume you will write to the conatiners

C++ unordered Associative Containers(C++11)

 

所以, 为了实现读操作, 我们采用如下方式:
C++ unordered Associative Containers(C++11)

 

关于associative array, 我们有一下几点需要注意:

既然associative array 既可以用unordered map实现, 有可以用map 实现, 那么我们该使用哪一个呢?

不难看出, 对于搜索操作, unordered map 需要O(1)时间。 对于map, 需要花费O(log(n)), 表面上看, 似乎unordered map 占优。 但是我们应该知道map的时间是guaranteed, 而unordered map 当退化的时候, 花费的时间变成了线性的了。

 

还有一点就是, 我们不能使用multimap 和 unordered multimap去实现关联数组, 因为这两个容器的key 不是unique 的。而且二者还没有[]运算符。

C++ unordered Associative Containers(C++11)

 

 

STL也提供了三个容器适配器, 如下:

C++ unordered Associative Containers(C++11)

 

 

关于容器, 还有另外的一个分类方法:

C++ unordered Associative Containers(C++11)

上面的程序中, 输出的结果是未定义的。 每当插入或者删除基于数组的容器的元素, 以前指向某个元素的指针的可能无效了(invalidate)。

 

C++ unordered Associative Containers(C++11),布布扣,bubuko.com

C++ unordered Associative Containers(C++11)

上一篇:python基础教程_学习笔记26:好玩的编程


下一篇:Merge array and hash in ruby if key appears in array