template < class Key, // unordered_set::key_type/value_type
class Hash = hash, // unordered_set::hasher
class Pred = equal_to, // unordered_set::key_equal
class Alloc = allocator // unordered_set::allocator_type
> class unordered_set;
无序集是不按特定顺序存储唯一元素的容器,它允许根据单个元素的值快速检索单个元素。在unordered_set中,元素的值同时是其键,它唯一地标识它。键是不可变的,因此,unordered_set中的元素不能在容器中修改一次 - 但是可以插入和删除它们。
在内部,unordered_set中的元素没有按任何特定顺序排序,但是根据它们的哈希值组织成桶,以允许通过它们的值直接快速访问各个元素(平均时间复杂度恒定)。
unordered_set容器比通过其键访问单个元素的set容器更快,尽管它们通过其元素子集的范围迭代通常效率较低。
unordered_set特性:
联想:关联容器中的元素由其键引用,而不是由它们在容器中的绝对位置引用。
无序:无序容器使用哈希表来组织其元素,这些哈希表允许通过键快速访问元素。
组:元素的值也是用于标识它的键。
独特的钥匙:容器中没有两个元素可以具有等效键。
分配器感知:容器使用allocator对象来动态处理其存储需求。
注意:unordered_set中的所有迭代器都指向const元素。
参数说明:
n -- 初始桶的最小数量。 这不是容器中元素的数量,而是构造时内部哈希表所需的最小插槽数。如果未指定此参数,则构造函数会自动确定(以某种方式取决于特定的库实现)
hf -- Hasher功能对象。 hasher是一个函数,它根据作为参数传递给它的容器对象键返回一个整数值.成员类型hasher在unordered_set中定义为其第二个模板参数(Hash)的别名。
eql -- 比较函数对象,如果作为参数传递的两个容器对象键被认为是相等的,则返回true。成员类型key_equal在unordered_set中定义为其第三个模板参数(Pred)的别名。
alloc -- 要使用的分配器对象而不是构造新的对象。
first last -- 将迭代器输入到范围中的初始位置和最终位置。使用的范围是[first,last),其中包括first和last之间的所有元素,包括first指向的元素,但不包括last指向的元素。
il -- initializer_list对象。 这些对象是从初始化列表声明符自动构造的。
构造函数例子:
// constructing unordered_sets
#include
#include
#include
template
T cmerge (T a, T b) { T t(a); t.insert(b.begin(),b.end()); return t; }
int main ()
{
std::unordered_set first; // empty
std::unordered_set second ( {"red","green","blue"} ); // init list
std::unordered_set third ( {"orange","pink","yellow"} ); // init list
std::unordered_set fourth ( second ); // copy
std::unordered_set fifth ( cmerge(third,fourth) ); // move
std::unordered_set sixth ( fifth.begin(), fifth.end() ); // range
std::cout << "sixth contains:";
for (const std::string& x: sixth) std::cout << " " << x;
std::cout << std::endl;
return 0;
}
输出:
sixth contains: pink yellow red green orange blue |
因为元素在unordered_set中根据key值,通过相应的hash()函数得到关键码值,关键码值对应着一个相应的位置,用该位置存储相应的信息。容器中的元素无特别的秩序关系,该容器允许基于值的快速元素检索,同时也支持正向迭代。在一个unordered_set内部,元素不会按任何顺序存储,而是通过元素值的哈希值,存放在各个槽中,(Bucker,也可以译为“桶”),这样就可以通过元素值快速找到各个值。
container iterator (1) | iterator begin() noexcept; const_iterator begin() const noexcept; |
---|---|
bucket iterator (2) | local_iterator begin ( size_type n ); const_local_iterator begin ( size_type n ) const; |
返回指向unordered_set容器(1)或其中一个存储桶(2)中的第一个元素的迭代器。请注意,unordered_set对象不保证哪个特定元素被视为其第一个元素。但是,在任何情况下,从开始到结束的范围都涵盖容器(或存储桶)中的所有元素,直到无效。
返回值:容器(1)或存储桶(2)中第一个元素的迭代器。
例子:
#include
#include
#include
int main()
{
std::unordered_set myset=
{"Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};
std::cout << "myset contains:";
for ( auto it = myset.begin(); it != myset.end(); ++it )
std::cout << " " << *it;
std::cout << std::endl;
std::cout<<"myset's bukets contain:\n";
for(unsigned int i=0;i
输出:
myset contains: Venus Jupiter Neptune Mercury Earth Uranus Saturn Mars myset's buckets contain: bucket #0 contains: bucket #1 contains: Venus bucket #2 contains: Jupiter bucket #3 contains: bucket #4 contains: Neptune Mercury bucket #5 contains: bucket #6 contains: Earth bucket #7 contains: Uranus Saturn bucket #8 contains: Mars bucket #9 contains: bucket #10 contains:
为啥同一个bucket存在多个值,因为它们的哈希值得到的关键码相同,冲突是不可避免的,而解决冲突的方法常见的有:开发地址法、再散列法、链地址法(也称拉链法)。而unordered_set内部解决冲突采用的是----链地址法,当用冲突发生时把具有同一关键码的数据组成一个链表。下图展示了链地址法的使用:
size_type bucket(const key_type&k)const;
找到元素的存储桶,返回值为K的元素所在的桶编号。
存储桶是容器内部哈希表中的一个插槽,根据其哈希值为其分配元素。存储桶的编号从0到(bucket_count -1)。
可以通过unordered_set :: begin和unordered_set :: end返回的范围迭代器访问存储桶中的各个元素。
// unordered_set::bucket
#include
#include
#include
int main ()
{
std::unordered_set myset = {"water","sand","ice","foam"};
for (const std::string& x: myset) {
std::cout << x << " is in bucket #" << myset.bucket(x) << std::endl;
}
return 0;
}