cpp►STL容器->哈希容器->unordered_set

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cpp►STL容器->哈希容器->unordered_set相关的知识,希望对你有一定的参考价值。

描述

std::unordered_set

template <class Key, class Hash = hash<Key>, 
          class Pred = equal_to<Key>, class Alloc = allocator<Key>>
class unordered_set;

unordered_set是不按特定顺序存储唯一元素的容器,它允许根据元素的值快速检索单个元素。

在无序的集合中,元素的值同时是它的键,唯一地标识它。键是不可变的,因此,无序集合中的元素一旦在容器中就不能被修改,但是它们可以被插入和删除。

在内部,unordered_set集合中的元素不是按任何特定顺序排序的,而是根据它们的散列值组织到储存区(buckets)中,以允许直接通过它们的值快速访问单个元素(平均时间复杂度不变)。

unordered_set容器比集合容器通过其键访问单个元素的速度更快,尽管它们通过元素的子集进行范围迭代的效率通常较低。

容器中的迭代器至少是正向迭代器。

无序容器使用哈希表(hash tables)组织它们的元素,哈希表允许通过它们的键快速访问元素。

< functional > std::hash

template <class T> struct hash;
一元函数对象类,标准库中定义和使用的默认哈希函数。
函数调用返回其参数的哈希值:哈希值是仅依赖于其参数的值,对于同一参数(对于给定的程序执行)始终返回相同的值。

< functional > std::equal_to

template <class T> struct equal_to;
二元函数对象类,其调用返回其两个参数比较是否相等(由operator==返回)。
一般来说,函数对象是定义了成员function operator()的类的实例。此成员函数允许对象使用与函数调用相同的语法。
其定义与以下行为相同:

template <class T> 
struct equal_to {
	bool operator() (const T& x, const T& y) const { 
		return x == y; 
	}
	typedef T first_argument_type;
	typedef T second_argument_type;
	typedef bool result_type;
};
模板参数(Template parameters)
  • Key
    元素的类型。unordered_set集合中的每个元素也由该值唯一标识。别名为成员类型unordered_set::key_type和unordered_set::value_type。
  • Hash
    一个一元函数对象类型,它以与元素类型相同的对象作为参数,并基于它返回一个类型为size_t的唯一值。这可以是实现了函数调用运算符的类,也可以是指向函数的指针。默认为hash<Key>,它返回一个哈希值,冲突概率(probability of collision)接近1.0/std::numeric_limits<size_t>::max()。
    unordered_set对象使用此函数返回的哈希值在内部组织其元素,从而加快查找单个元素的过程。
    别名为成员类型unordered_set::hasher。
  • Pred
    一个二元谓词,它接受两个与元素类型相同的参数并返回布尔值。表达式pred(a, b),其中pred是这种类型的对象,a和b是键值,如果a被认为等同于b,则应返回true。这可以是实现了函数调用运算符的类,也可以是指向函数的指针。默认为equal_to(Key),它返回与equal-to运算符(a==b)相同的值。
    unordered_set对象使用这个表达式来确定两个元素键是否相等。unordered_set容器中的两个元素都不能有使用此谓词生成true的键。
    别名为成员类型unordered_set::key_equal。
  • Alloc
    用于定义存储分配模型的分配器对象的类型。默认情况下,使用allocator类模板,它定义了最简单的内存分配模型,并且与值无关。
    别名为成员类型unordered_set::allocator_type。
成员类型(Member types)
成员类型定义备注
key_type第一个模板参数(Key)
value_type第一个模板参数(Key)与key_type相同
hasher第二个模板参数(Hash)默认为:hash<key_type>
key_equal第三个模板参数(Pred)默认为:equal_to<key_type>
pointerAlloc::pointer对于默认allocator,为:value_type*
local_iterator一个针对const value_type的forward iterator(前向迭代器)可转换为const_local_iterator
成员函数(Member functions)

构造函数:

// constructing unordered_sets
#include <iostream>
#include <string>
#include <unordered_set>
template<class T>
T cmerge(T a, T b) { 
	T t(a); 
	t.insert(b.begin(), b.end()); 
	return t; 
}
int main() {
	std::unordered_set<std::string> first;// empty
	std::unordered_set<std::string> second({ "red","green","blue" });// init list
	std::unordered_set<std::string> third({ "orange","pink","yellow" });// init list
	std::unordered_set<std::string> fourth(second);// copy
	std::unordered_set<std::string> fifth(cmerge(third, fourth));// move
	std::unordered_set<std::string> sixth(fifth.begin(), fifth.end());// range

	std::cout << "sixth contains:";
	for (const std::string& x : sixth) 
		std::cout << " " << x;
	return 0;
}
sixth contains: pink yellow red green orange blue
迭代器(Iterators)
  • begin、end
container iterator (1)	iterator begin() noexcept;
bucket iterator (2)     local_iterator begin(size_type n);

对于bucket iterator (2),参数n表示bucket的编号,它应该小于bucket_count。
它是一个可选参数,用于更改此成员函数的行为:如果设置了,则检索到的迭代器将指向编号为n的bucket的第一个元素,否则将指向容器container的第一个元素。
成员类型size_type是无符号整数类型。

Buckets
  • bucket_count
    size_type bucket_count() const noexcept;
    bucket是容器内部哈希表中的一个槽,元素根据其哈希值被分配到该槽中。
    bucket的数量直接影响容器哈希表的负载因子(load factor)(从而影响冲突的概率)。容器会自动增加bucket的数量,以将负载系数保持在特定阈值(其最大负载系数max_load_factor)以下,从而导致每次需要增加bucket的数量时重新刷新。
// unordered_set::bucket_count
#include <iostream>
#include <string>
#include <unordered_set>
int main() {
	std::unordered_set<std::string> myset =
	{ "Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune" };

	unsigned n = myset.bucket_count();

	std::cout << "myset has " << n << " buckets.\\n";

	for (unsigned i = 0; i < n; ++i) {
		std::cout << "bucket #" << i << " contains:";
		for (auto it = myset.begin(i); it != myset.end(i); ++it)
			std::cout << " " << *it;
		std::cout << "\\n";
	}
	return 0;
}

一种可能的输出:

myset has 11 buckets.
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:

另一种可能的输出:

myset has 8 buckets.
bucket #0 contains: Mars
bucket #1 contains:
bucket #2 contains: Neptune
bucket #3 contains:
bucket #4 contains:
bucket #5 contains: Uranus
bucket #6 contains: Saturn Jupiter Venus Mercury
bucket #7 contains: Earth
  • max_bucket_count
    size_type max_bucket_count() const noexcept;
    返回unordered_set容器可以拥有的最大bucket数目。
// unordered_set limits
#include <iostream>
#include <unordered_set>
int main() {
	std::unordered_set<int> myset;
	std::cout << "max_size = " << myset.max_size() << std::endl;
	std::cout << "max_bucket_count = " << myset.max_bucket_count() << std::endl;
	std::cout << "max_load_factor = " << myset.max_load_factor() << std::endl;
	return 0;
}
max_size = 357913941
max_bucket_count = 536870911
max_load_factor = 1
  • bucket_size
    size_type bucket_size(size_type n) const;
    返回在编号为n的bucket中的元素个数。
// unordered_set::bucket_size
#include <iostream>
#include <string>
#include <unordered_set>
int main() {
	std::unordered_set<std::string> myset =
	{ "red", "green", "blue", "yellow", "purple", "pink" };

	unsigned nbuckets = myset.bucket_count();
	std::cout << "myset has " << nbuckets << " buckets:\\n";
	for (unsigned i = 0; i < nbuckets; ++i) {
		std::cout << "bucket #" << i << " has " << myset.bucket_size(i) << " elements.\\n";
	}
	return 0;
}

可能的一种输出:

myset has 8 buckets:
bucket #0 has 0 elements.
bucket #1 has 1 elements.
bucket #2 has 0 elements.
bucket #3 has 0 elements.
bucket #4 has 2 elements.
bucket #5 has 2 elements.
bucket #6 has 0 elements.
bucket #7 has 1 elements.
  • bucket
    size_type bucket(const key_type& k) const;
    返回值为k的元素所在的bucket编号。
    bucket被编号为0~(bucket_count-1)。
// unordered_set::bucket
#include <iostream>
#include <string>
#include <unordered_set>
int main() {
    std::unordered_set<std::string> myset = 
    	{ "water","sand","ice","foam" };
    for (const std::string& x : myset) {
        std::cout << x << " is in bucket #" << myset.bucket(x) << std::endl;
    }
    return 0;
}

可能的一种输出:

ice is in bucket #0
water is in bucket #0
sand is in bucket #3
foam is in bucket #4
哈希策略(Hash policy)
  • load_factor、max_load_factor
    负载系数是容器中元素的数量(size)与bucket的数量(bucket_count)之间的比率:
    load_factor = size / bucket_count
    负载因子影响哈希表中的冲突概率(即两个元素位于同一个bucket中的概率)。容器会自动增加bucket的数量,以将负载系数保持在特定阈值(其最大负载系数max_load_factor)以下,这导致在每次需要扩展时重新刷新。
// unordered_set hash table stats
#include <iostream>
#include <unordered_set>
int main() {
	std::unordered_set<int> myset;
	std::cout << "size = " << myset.size() << std::endl;
	std::cout << "bucket_count = " << myset.bucket_count() << std::endl;
	std::cout << "load_factor = " << myset.load_factor() << std::endl;
	std::cout << "max_load_factor = " << myset.max_load_factor() << std::endl;
	return 0;
}

Possible output:

size = 0
bucket_count = 8
load_factor = 0
max_load_factor = 1
  • rehash
    void rehash(size_type n);
    设置容器哈希表中bucket的最小数量为n。
    如果n大于容器中当前的bucket数量(bucket_count),则强制重新哈希(rehash)。新的bucket数可以等于或大于n。如果n小于容器中当前的bucket数量,该函数可能对bucket数量没有影响,并且可能不会强制重新哈希。
    rehash是哈希表的重建:容器中的所有元素根据它们的哈希值重新排列到新的一组bucket中。这可能会改变容器中元素的迭代顺序。
    在一个操作中,当容器的负载系数超过其最大负载系数时(load_factor>max_load_factor),容器会自动执行rehash。
    请注意,此函数期望以bucket数作为参数。存在一个类似的函数unordered_set::reserve,它期望以容器中的元素个数作为参数。
  • reserve
    void reserve(size_type n);
    将容器中的bucket的数量(bucket_count)设置为最适合包含至少n个元素。
观察者(Observers)
  • hash_function
    hasher hash_function() const;
    返回unordered_set容器使用的哈希函数对象。
    哈希函数是一个一元函数,它以key_type类型的对象作为参数,并基于它返回类型为size_t的唯一值。
// unordered_set::hash_function
#include <iostream>
#include <string>
#include <unordered_set>
typedef std::unordered_set<std::string> stringset;
int main() {
	stringset myset;
	stringset::hasher fn = myset.hash_function();
	std::cout << "that: " << fn("that") << std::endl;
	std::cout << "than: " << fn("than") << std::endl;
	return 0;
}
that: 3407360812
than: 3843578906
  • key_eq
    key_equal key_eq() const;
    返回unordered_set容器使用的键等价比较谓词。
    键等价比较是一个谓词,它将两个元素的值作为参数,并返回一个bool值,指示它们是否被认为是等价的。
    成员类型key_equal是容器使用的键等价比较谓词的类型,在unordered_set中定义为其第三个模板参数(Pred)的别名。
// unordered_set::key_eq
#include <iostream>
#include <string>
#include <unordered_set>
int main() {
	std::unordered_set<std::string> myset;
	bool case_insensitive = myset.key_eq()("checking", "CHECKING");
	std::cout << "myset.key_eq() is ";
	std::cout << (case_insensitive ? "case insensitive" : "case sensitive");
	std::cout << std::endl;
	return 0;
}
myset.key_eq() is case sensitive

以上是关于cpp►STL容器->哈希容器->unordered_set的主要内容,如果未能解决你的问题,请参考以下文章

cpp►STL容器->排序容器->map

cpp►STL容器->排序容器->set

cpp►STL容器->序列容器->list

cpp►STL容器->排序容器->multiset

cpp►STL容器->序列容器->deque

cpp►STL容器->排序容器->multimap