unordered_map和unordered_set

一、哈希表

1.使用哈希的前提

在实际编程中,我们常常面临着两个问题:存储和查询,这两个过程的效率往往制约着整个程序的效率,而我们常见的存储数据的数据结构比如线性表,树,图等,数据在结构中的位置往往是不明确的,当我们在这些数据结构中要查询一个数据,都避免不了去执行查询算法,去遍历数据结构,拿关键字和结构中的数据进行一一比较,从而得到想要的数据,我们就希望能不能不通过比较就能获得我们想要的结果呢?

答案是有的,不通过任何比较,一次存储便能取得所查记录,但这就必须在记录的存储位置和他的关键字之间建立一个确定的对应关系f,使得每个关键字和结构中的唯一的存储位置相对应,这个关系就是我们所说的哈希函数f(x),在这个思想上建立起来的表就成为哈希表

2.介绍

哈希表是一种使用哈希函数组织数据,以支持快速插入和搜索的数据结构。

有两种不同类型的哈希表:哈希集合和哈希映射

哈希集合是集合数据结构的实现之一,用于存储非重复值
哈希映射是映射数据结构的实现之一,用于存储(key, value)键值对
在标准模板库的帮助下,哈希表是易于使用的。大多数常见语言(如Java,C ++ 和 Python)都支持哈希集合和哈希映射。

通过选择合适的哈希函数,哈希表可以在插入和搜索方面实现出色的性能。

原理
哈希表的关键思想是使用哈希函数将键映射到存储桶。更确切地说,

当我们插入一个新的键时,哈希函数将决定该键应该分配到哪个桶中,并将该键存储在相应的桶中;
当我们想要搜索一个键时,哈希表将使用相同的哈希函数来查找对应的桶,并只在特定的桶中进行搜索。

哈希映射

在示例中,我们使用 y = x % 5 作为哈希函数。让我们使用这个例子来完成插入和搜索策略:

插入:我们通过哈希函数解析键,将它们映射到相应的桶中。

例如,1987 分配给桶 2,而 24 分配给桶 4。
搜索:我们通过相同的哈希函数解析键,并仅在特定存储桶中搜索。

如果我们搜索 1987,我们将使用相同的哈希函数将1987 映射到 2。因此我们在桶 2 中搜索,我们在那个桶中成功找到了 1987。
例如,如果我们搜索 23,将映射 23 到 3,并在桶 3 中搜索。我们发现 23 不在桶 3 中,这意味着 23 不在哈希表中。

3.STL的使用

#include <unordered_set>                // 当使用set时引用的模板库

int main() {
    // 创建一个哈希集合
    unordered_set<int> hashset;   
    // 插入新的关键字
    hashset.insert(3);
    hashset.insert(2);
    hashset.insert(1);
    // 删除关键字
    hashset.erase(2);
    // 判断关键字是否在哈希集合中,如果在方法返回负数。如果在集合中返回真,否则返回假
    if (hashset.count(2) <= 0) {
        cout << "关键字2不在哈希集合中" << endl;
    }
    // 得到哈希集合的大小
    cout << "The size of hash set is: " << hashset.size() << endl; 
    // 遍历哈希集合,注意要以指针的形式
    for (auto it = hashset.begin(); it != hashset.end(); ++it) {
        cout << (*it) << " ";
    }
    cout << "are in the hash set." << endl;
    // 清除哈希集合
    hashset.clear();
    // 判断哈希结合是否为空
    if (hashset.empty()) {
        cout << "hash set is empty now!" << endl;
    }
}

二、unordered_map和unordered_set

(一)、实现原理

unordered_map内部实现了一个哈希表,也叫散列表,通过把关键码值映射到Hash表中一个位置来访问记录,查找的时间复杂度可达到O(1),其在海量数据处理中有着广泛应用。因此,其元素<key,value>的排列顺序是无序的。
unordered_set底层也是哈希表,只是存储的是value,而不是<key,value>

c++中对unordered_set描述大体如下:无序集合容器(unordered_set)是一个存储唯一(unique,即无重复)的关联容器(Associative container),容器中的元素无特别的秩序关系,该容器允许基于值的快速元素检索,同时也支持正向迭代。
在一个unordered_set内部,元素不会按任何顺序排序,而是通过元素值的hash值将元素分组放置到各个槽(Bucker,也可以译为“桶”),这样就能通过元素值快速访问各个对应的元素(均摊耗时为O(1))。

就是说我们只需要通过某个函数将关键字和其存储位置相对应起来即可:

​ 存储位置 = f(关键字)

我们通过查找关键字不需要比较就可以获得需要记录的存储位置,这是一种新的存储技术----散列存储技术(将记录存储到其关键字对应的f(key)内存位置)

这种对应关系f称为哈希函数(Hash),然后以这种散列存储方式将记录存储到一块连续的存储空间中,这块连续存储空间称为散列表或者哈希表(Hash Table).

(二)、内部公共接口

1.查找

1.count(key):通过key查找,找到返回1,找不到返回0。
    
2.find(key):返回key对应的迭代器,如果key不存在,则find返回unordered_map::end因此可以通过判断map.find(key) == map.end()来判断,key是否存在于当前的unordered_map中,    

2.迭代器--iterator

unordered_map
/*
c++ 里面的map容器的迭代器里面 有个first 和 second
例如
map<string, int> m; <key,value>
m["one"] = 1;

map<string, int>::iterator p = m.begin();
p->first; // 这个是 string 值是 "one"
p->second; //这个是 int 值是 1
*/

unordered_set
/*
函数声明	功能介绍
begin	   返回unordered_set第一个元素的迭代器
end	       返回unordered_set最后一个元素下一个位置的迭代器
cbegin	   返回unordered_set第一个元素的const迭代器
cend	   返回unordered_set最后一个元素下一个位置的const迭代器
*/

3.其他函数

unordered_set
/*

函数声明							功能介绍 
bool empty() const					  检测unordered_set是否为空
size_t size() const					  获取unordered_set的有效元素个数
iterator find(const key_type& k)	   返回k在哈希桶中的位置
size_t count(const key_type& k)		   返回哈希桶中关键码为k的键值对的个数
insert								 向容器中插入键值对
erase								 删除容器中的键值对
void clear()						 清空容器中有效元素个数
void swap(unordered_set&)			  交换两个容器中的元素
size_t bucket_count()const			  返回哈希桶中桶的总个数
size_t bucket_size(size_t n)const	  返回n号桶中有效元素的总个数
size_t bucket(const key_type& k)	  返回元素key所在的桶号

*/

4.unordered_set的构造函数

unordered_set<int> set1; //创建空set
unordered_set<int> set2(set1);    //拷贝构造,将set1的数据赋值给set2
unordered_set<int> set3(set1.begin(), set1.end());    //迭代器构造,如果有重复的,自动删除
unordered_set<int> set4(arr,arr+5);    //数组构造
unordered_set<int> set5(move(set2));    //移动构造
unordered_set<int> set6 {1,2,10,10};//使用initializer_list初始化

5.常用函数

set1.find(2);    //查找2,找到返回迭代器,失败返回end()
set1.count(2);    //返回指2出现的次数,0或1
set1.emplace(3);    //使用转换移动构造函数,返回pair<unordered_set<int>::iterator, bool>
set1.insert(3);    //插入元素,返回pair<unordered_set<int>::iterator, bool>
set1.insert({1,2,3});    //使用initializer_list插入元素
set1.insert(set1.end(), 4);//指定插入位置,如果位置正确会减少插入时间,返回指向插入元素的迭代器
set1.insert(set2.begin(), set2.end());//使用范围迭代器插入
set1.erase(1);	    //删除操作,成功返回1,失败返回0
set1.erase(set1.find(1));	    //删除操作,成功返回下一个pair的迭代器
set1.erase(set1.begin(), set1.end());    //删除set1的所有元素,返回指向end的迭代器
set1.empty();        //是否为空
set1.size();        //大小
set1.bucket_count();    //返回容器中的桶数
set1.bucket_size(1);    //返回1号桶中的元素数
set1.bucket(1);    //1在哪一个桶
set1.load_factor();    //负载因子,返回每个桶元素的平均数,即size/float(bucket_count);
set1.max_load_factor();//返回最大负载因子
set1.max_load_factor(2);//设置最大负载因子为2,rehash(0)表示强制rehash
set1.rehash(20);//设置桶的数量为20,并且重新rehash
set1.reserve(20);//将容器中的桶数设置为最适合元素个数,如果20大于当前的bucket_count乘max_load_factor,则增加容器的bucket_count并强制重新哈希。如果20小于该值,则该功能可能无效。
unordered_set<int>::iterator it = set1.begin();	    //返回指向set1首元素的迭代器
unordered_set<int>::const_iterator c_it = set1.cbegin();	    //返回指向set1首元素的常量迭代器
unordered_set<int>::local_iterator it = set1.begin(1);//返回1号桶中的首元素迭代器
unordered_set<int>::const_local_iterator c_it = set1.cbegin(1);//返回1号桶中的首元素的常量迭代器
pair<unordered_set<int>::iterator, unordered_set<int>::iterator> it = set1.equal_range(1);//返回一个pair,pair里面第一个变量是lower_bound返回的迭代器,第二个迭代器是upper_bound返回的迭代器
set1.clear();        //清空
posted @ 2021-04-09 17:04  kisfly  阅读(3160)  评论(0编辑  收藏  举报