目录
哈希表
定义
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
记录的存储位置 = f(关键字)
这里的对应关系 f 称为散列函数,又称为哈希(Hash函数),采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表(Hash table)。
哈希表hashtable(key,value) 就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。
(或者:把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。)
而当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位。
https://blog.csdn.net/duan19920101/article/details/51579136
举例:
散列函数:由 w -> 王 F()
在哈希表中是通过哈希函数将一个值映射到另外一个值的,所以在哈希表中,a映射到b,a就叫做键值,而b呢?就叫做a的哈希值,也就是hash值。
处理哈希冲突
1、 开放寻址法
1.1 线性探查法
1.2 平方探测法
2、 拉链法
这里采用的是链表,什么意思呢?就像图中所示,现在张三和李四都要放在1找个位置上,但是张三先来的,已经占了这个位置,待在了这个位置上了,那李四呢?解决办法就是链表,这时候这个1的位置存放的不单单是之前的那个Entry了,此时的Entry还额外的保存了一个next指针,这个指针指向数组外的另外一个位置,将李四安排在这里,然后张三那个Entry中的next指针就指向李四的这个位置,也就是保存的这个位置的内存地址,如果还有冲突,那就把又冲突的那个Entry放在一个新位置上,然后李四的Entry中的next指向它,这样就形成了一个链表。
哈希表的扩容
哈希表如何读取数据
散列表的构造方法
1、 直接定址法
2、 除留取余法
3、数字分析法
4、折叠法
5、平方取中法
C++ STL 之 unordered_set 使用
定义
C++ 11中对unordered_set描述大体如下:无序集合容器(unordered_set)是一个存储唯一(unique,即无重复)的关联容器(Associative container),容器中的元素无特别的秩序关系,该容器允许基于值的快速元素检索,同时也支持正向迭代。
在一个unordered_set内部,元素不会按任何顺序排序,而是通过元素值的hash值将元素分组放置到各个槽(Bucker,也可以译为“桶”),这样就能通过元素值快速访问各个对应的元素(均摊耗时为O(1))。
原型中的Key代表要存储的类型,而hash<Key>也就是你的hash函数,equal_to<Key>用来判断两个元素是否相等,allocator<Key>是内存的分配策略。一般情况下,我们只关心hash<Key>和equal_to<Key>参数。
unordered_set本质是使用hash散列的方式存储数据,是一种使用hash值作为key的容器,所以当有频繁的搜索、插入和移除拥有常数时间。unordered_set存储原理是声明一个有n个桶的数据结构,计算加入到unordered_set的新的值hash,然后计算hash%n后的值x,将新的值加入到桶x中。当桶x中已经有了元素,就直接链接在后边。当数据结构中的元素满足一定数量时我们要扩充桶的数量,并重新构建桶结构。
unordered_set是一种无序集合,既然跟底层实现基于hashtable那么它一定拥有快速的查找和删除,添加的优点.基于hashtable当然就失去了基于rb_tree的自动排序功能。
unordered_set无序,所以在迭代器的使用上,set的效率会高于unordered_set。
模板原型
template < class Key,
class Hash = hash<Key>,
class Pred = equal_to<Key>,
class Alloc = allocator<Key>
> class unordered_set;
基本函数
1. unordered_set构造
std::unordered_set<std::string> c; //初始化容器 unordered_set<string> c;
std::unordered_set<std::string> c{"aaa","bbb","ccc"}; // 初始化容器,并将“aaa”、“bbb”、“ccc”加入到容器中
std::unordered_set<std::string> c{16}; //初始化容器,并设置16个桶
2. 添加新的元素(无法插入相同的元素)
c.insert("dddd"); //向容器添加元素”dddd"
a.insert({ "aaa","bbbb","cccc" }); //向容器添加元素"aaa","bbbb","cccc"
a.insert(b.begin(), b.end()); //b是一个存储着和a相同类型元素的向量,可将b中所有元素添加到a中
3. 查找元素
a.find("eeee"):查找元素"eeee",返回结果为a.end()则表明没有找到,否则返回所对应元素
a.count("eeee"):查找元素"eeee"在a中有几个(由于unordered_set中没有相同的元素,所以结果通常为0或1) ??
4.查找桶接口
a.bucket_count():返回数据结构中桶的数量
a.bucket_size(i):返回桶i中的大小
a.bucket(“eeee"):返回元素"eeee"在哪个桶里
存储桶是unordered_set内部哈希表中的一个存储元素的插槽。
注意:unordered_set中的存储桶从0到n-1编号,其中n是存储桶的总数。
参数:此函数不接受任何参数。
返回值:此函数返回unordered_set容器中存在的存储桶的当前计数。
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
unordered_set<int> sampleSet;
// Inserting elements
sampleSet.insert(5);
sampleSet.insert(10);
sampleSet.insert(15);
sampleSet.insert(20);
sampleSet.insert(25);
cout << "The sampleSet container has " << sampleSet.bucket_count()
<< " number of buckets\n\n";
for (auto itr = sampleSet.begin(); itr != sampleSet.end(); itr++) {
cout << "The Element " << (*itr) << " is present in the bucket: "
<< sampleSet.bucket(*itr);
cout << endl;
}
return 0;
}
输出:
The sampleSet container has 11 number of buckets
The Element 25 is present in the bucket: 3
The Element 5 is present in the bucket: 5
The Element 10 is present in the bucket: 10
The Element 15 is present in the bucket: 4
The Element 20 is present in the bucket: 9
5. 观察器
a.hash_function()("aaa"):返回"aaa"所对应的hash值
a.key_eq()("aaa","aaaa") :当元素相同时返回true,否则返回false
6. 清除元素
a.clear():清除a中所有元素
a.erase("aaa"):清除元素"aaa"
7. 其他函数
a.size():返回a中总的元素个数
a.max_size():返回a中最大容纳元素
a.empty():判断a中是否为空
基本操作
1. 迭代器操作
//返回头迭代器 begin()
unordered_set<int>::iterator ite_begin = c1.begin();
//返回尾迭代器 end()
unordered_set<int>::iterator ite_end = c1.end();
//返回const头迭代器 cbegin()
unordered_set<int>::const_iterator const_ite_begin = c1.cbegin();
//返回const尾迭代器 cend()
unordered_set<int>::const_iterator const_ite_end = c1.cend();
//槽迭代器
unordered_set<int>::local_iterator local_iter_begin = c1.begin(1);
unordered_set<int>::local_iterator local_iter_end = c1.end(1);
2. 基本操作
//查找函数 find() 通过给定主键查找元素
unordered_set<int>::iterator find_iter = c1.find(1);
//value出现的次数 count() 返回匹配给定主键的元素的个数
c1.count(1);
//返回元素在哪个区域equal_range() 返回值匹配给定搜索值的元素组成的范围
pair<unordered_set<int>::iterator, unordered_set<int>::iterator> pair_equal_range = c1.equal_range(1);
//插入函数 emplace()
c1.emplace(1);
//插入函数 emplace_hint() 使用迭代器
c1.emplace_hint(ite_begin, 1);
//插入函数 insert()
c1.insert(1);
//删除 erase()
c1.erase(1);//1.迭代器 value 区域
//清空 clear()
c1.clear();
//交换 swap()
c1.swap(c2);
3. 篮子操作
//篮子操作 篮子个数 bucket_count() 返回槽(Bucket)数
c1.bucket_count();
//篮子最大数量 max_bucket_count() 返回最大槽数
c1.max_bucket_count();
//篮子个数 bucket_size() 返回槽大小
c1.bucket_size(3);
//返回篮子 bucket() 返回元素所在槽的序号
c1.bucket(1);
// load_factor 返回载入因子,即一个元素槽(Bucket)的最大元素数
c1.load_factor();
// max_load_factor 返回或设置最大载入因子
c1.max_load_factor();
4. 内存操作
// rehash 设置槽数
c1.rehash(1);
// reserve 请求改变容器容量
c1.reserve(1000);
5. hash func
//hash_function() 返回与hash_func相同功能的函数指针
auto hash_func_test = c1.hash_function();
//key_eq() 返回比较key值得函数指针
auto key_eq_test = c1.key_eq();
unordered_map的使用
1、 介绍
关联容器,内部采用的是hash表结构,拥有快速检索的功能。
2、 特性:
- 关联性:通过key去检索value,而不是通过绝对地址(和顺序容器不同)
- 无序性:使用hash表存储,内部无序
- Map : 每个值对应一个键值
- 键唯一性:不存在两个元素的键一样
- 动态内存管理:使用内存管理模型来动态管理所需要的内存空间
3、 迭代器
unordered_map的迭代器是一个指针,指向这个元素,通过迭代器来取得它的值。
unordered_map<Key,T>::iterator it;
(*it).first; // the key value (of type Key)
(*it).second; // the mapped value (of type T)
(*it); // the "element value" (of type pair<const Key,T>)
它的键值分别是迭代器的first和second属性。
it->first; // same as (*it).first (the key value)
it->second; // same as (*it).second (the mapped value)
4、 操作
- size() // 返回unordered_map的大小
- bool empty() const noexcept; // 为空返回true
- iterator find ( const key_type& k ); //查找key所在的元素。
- 找到:返回元素的迭代器。通过迭代器的second属性获取值
- 没找到:返回unordered_map::end - insert //插入
- mapped_type& at ( const key_type& k ); //
查找key所对应的值
- 如果存在:返回key对应的值,可以直接修改,和[]操作一样。
- 如果不存在:抛出 out_of_range 异常.mymap.at(“Mars”) = 3396; //mymap[“Mars”] = 3396
-
erase() // 通过位置(迭代器)iterator erase ( const_iterator position );
- 通过key size_type erase ( const key_type& k );
- 通过范围(两个迭代器) iterator erase ( const_iterator first, const_iterator last );
-
clear() //清空unordered_map
-
swap () //交换两个unordered_map(注意,不是交换特定元素,是整个交换两个map中的所有元素)
5、 举例代码
插入--查找--修改--删除--交换--清空
// unordered_map::insert
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;
void display(unordered_map<string,double> myrecipe,string str)
{
cout << str << endl;
for (auto& x: myrecipe)
cout << x.first << ": " << x.second << endl;
cout << endl;
}
int main ()
{
unordered_map<string,double>
myrecipe,
mypantry = {{"milk",2.0},{"flour",1.5}};
/****************插入*****************/
pair<string,double> myshopping ("baking powder",0.3);
myrecipe.insert (myshopping); // 复制插入
myrecipe.insert (make_pair<string,double>("eggs",6.0)); // 移动插入
myrecipe.insert (mypantry.begin(), mypantry.end()); // 范围插入
myrecipe.insert ({{"sugar",0.8},{"salt",0.1}}); // 初始化数组插入(可以用二维一次插入多个元素,也可以用一维插入一个元素)
myrecipe["coffee"] = 10.0; //数组形式插入
display(myrecipe,"myrecipe contains:");
/****************查找*****************/
unordered_map<string,double>::const_iterator got = myrecipe.find ("coffee");
if ( got == myrecipe.end() )
cout << "not found";
else
cout << "found "<<got->first << " is " << got->second<<"\n\n";
/****************修改*****************/
myrecipe.at("coffee") = 9.0;
myrecipe["milk"] = 3.0;
display(myrecipe,"After modify myrecipe contains:");
/****************擦除*****************/
myrecipe.erase(myrecipe.begin()); //通过位置
myrecipe.erase("milk"); //通过key
display(myrecipe,"After erase myrecipe contains:");
/****************交换*****************/
myrecipe.swap(mypantry);
display(myrecipe,"After swap with mypantry, myrecipe contains:");
/****************清空*****************/
myrecipe.clear();
display(myrecipe,"After clear, myrecipe contains:");
return 0;
}
输出结果:
myrecipe contains:
salt: 0.1
milk: 2
flour: 1.5
coffee: 10
eggs: 6
sugar: 0.8
baking powder: 0.3
found coffee is 10
After modify myrecipe contains:
salt: 0.1
milk: 3
flour: 1.5
coffee: 9
eggs: 6
sugar: 0.8
baking powder: 0.3
After erase myrecipe contains:
flour: 1.5
coffee: 9
eggs: 6
sugar: 0.8
baking powder: 0.3
After swap with mypantry, myrecipe contains:
flour: 1.5
milk: 2
After clear, myrecipe contains:
6、 迭代器和bucket操作
6.1 begin
iterator begin() noexcept;
local_iterator begin ( size_type n );
- begin() : 返回开始的迭代器(和你的输入顺序没关系,因为它的无序的)
- begin(int n) : 返回n号bucket的第一个迭代器
6.2 end
iterator end() noexcept;
local_iterator end( size_type n );
- end(): 返回结束位置的迭代器
- end(int n) : 返回n号bucket的最后一个迭代器
6.3 bucket
size_type bucket ( const key_type& k ) const;
返回通过哈希计算key所在的bucket(注意:这里仅仅做哈希计算确定bucket,并不保证key一定存在bucket中!)
6.4 bucket_count
返回bucket的总数
6.5 bucket_size
size_type bucket_size ( size_type n ) const;
返回第i个bucket的大小(这个位置的桶子里有几个元素,注意:函数不会判断n是否在count范围内)
// unordered_map::bucket_count
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;
int main ()
{
unordered_map<string,string> mymap =
{
{"house","maison"},
{"apple","pomme"},
{"tree","arbre"},
{"book","livre"},
{"door","porte"},
{"grapefruit","pamplemousse"}
};
/************begin和end迭代器***************/
cout << "mymap contains:";
for ( auto it = mymap.begin(); it != mymap.end(); ++it )
cout << " " << it->first << ":" << it->second;
cout << endl;
/************bucket操作***************/
unsigned n = mymap.bucket_count();
cout << "mymap has " << n << " buckets.\n";
for (unsigned i=0; i<n; ++i)
{
cout << "bucket #" << i << "'s size:"<<mymap.bucket_size(i)<<" contains: ";
for (auto it = mymap.begin(i); it!=mymap.end(i); ++it)
cout << "[" << it->first << ":" << it->second << "] ";
cout << "\n";
}
cout <<"\nkey:'apple' is in bucket #" << mymap.bucket("apple") <<endl;
cout <<"\nkey:'computer' is in bucket #" << mymap.bucket("computer") <<endl;
return 0;
}
7、与map的区别
map: map内部实现了一个红黑树,该结构具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素,因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行这样的操作,故红黑树的效率决定了map的效率。
unordered_map: unordered_map内部实现了一个哈希表,因此其元素的排列顺序是杂乱的,无序的