当前位置:   article > 正文

c++中的hash_map_c++ hashmap

c++ hashmap


最近刷左神算法,hash_map经常被用到(hash_map的增删改查时间复杂度都能低至O(1))。但是左神用的都是java中的hash_map,苦于这个原因,只能自己研究一下C++中的hash_map。
参考:https://blog.csdn.net/u010025211/article/details/46653519?utm_medium=distribute.pc_relevant_bbs_down.none-task–2 allsobaiduend default-1.nonecase&depth_1-utm_source=distribute.pc_relevant_bbs_down.none-task–2all sobaiduenddefault-1.nonecase

hash相关

哈希函数

经典哈希函数的几个特征:

  • 输入域无限大
  • 输出域有界
  • 输入参数是固定的情况下,返回值也是固定的
  • 输入参数不同时,返回值可能是相同的(哈希冲突)
  • 当输入参数很多时,输出的每个数的概率大致是均匀的
    下图帮助理解这个东东:
    在这里插入图片描述
    如上图,输入为0~98,一共99个数。经过哈希函数后,得到的hash_code为0,1,2。那么0,1,2分别对应输入的33个数。

哈希函数的推广
当若干输入经过哈希函数,得到均匀分布的hash_code,给这些hash_code取模m,则取模后的hash_code在0~m-1上也是均匀分布的。
如何产生1000个独立的哈希函数
将哈希函数的返回值分为两份,如返回值为h,其范围为2^64 =16位 ,则将h分为8位的h_1,与8为的h_2。由哈希函数的特点可知,h_1,h_2是相互独立的。因此令h_3=h_1+h_2;h_4=h_1+ 2Xh_2;…,h_1000=h_1+998Xh_2;即可

哈希表

经典哈希表的建立过程:
若输入为put(A,0),A为键值,0为实值。若要建立大小为17的hash表(可以用一个数组来实现),将A经过哈希函数得到的hash_code取模与17,则结果必为0~16中的某值。将(A,0)放入这个空间(数组)中对应的下标。如下图:
在这里插入图片描述
如上图,(A,0)放入10对应的下标中。接下来,我们采用链式存储的结构(采用该结构也有效解决了哈希冲突的问题)。将(B,1),(C,2),… ;放入哈希表中。若取模结果重复,难么判断键值是否重复,若重复,则覆盖原来的数据,若不重复则将数据接到原来数据块的后面。如下图:
在这里插入图片描述
接下来,针对上述建立哈希表的过程的c++代码实现,以及实现删除,查找操作进行代码实现。
代码懒得弄了,我上传了。

c++中的hash_map

c++现在已经使用unordered_map来代替hash_map了,因此这里主要研究unordered_map。实际上unordered_map就相当于java中的hash_map。为了阅读方便,我就直接写成hash_map了。
“直接定址”与“解决冲突”是哈希表的两大特点。hash_map中直接地址用hash函数生成,解决冲突,用比较函数解决。由此可见,要使用哈希表, 和用户相关的是:hash函数和比较函数

在声明自己的哈希函数时要注意以下几点:

  • 使用struct,然后重载operator().
  • 返回是size_t
  • 参数是你要hash的key的类型。
  • 函数是const类型的。
  • 方法:一般自定义类型可能包含几种内置类型,我们可以分别计算出内置类型的 Hash Value 然后对它们进行结合得到一个哈希值,一般直接采用移位加异或(XOR)便可得到还不错的哈希值(碰撞不会太频繁),

比较函数的实现
<1> 重载==运算符
<2> 仿函数

键值为基本数据类型的unordered_map使用
在SGI STL中,提供了以下hash函数:

struct hash<char*>
struct hash<const char*>
struct hash<char> 
struct hash<unsigned char> 
struct hash<signed char>
struct hash<short>
struct hash<unsigned short> 
struct hash<int> 
struct hash<unsigned int>
struct hash<long> 
struct hash<unsigned long>

好在,系统为string也提供了hash函数hash< string >,因此当键值为string时,不用自己写哈希函数,如下,分别为基本数据类型和自定义数据类型使用unordered_map

#include<iostream>
#include<unordered_map>
#include <string>
using namespace std;
void test1(){
 unordered_map<string, int> map;
 hm["孙"]=0;
 hm["李"]=1;
 hm["张"]=2;
 for (auto h : map)
 {
  	cout << h.first << "->" << h.second << endl;
 }
}
void test2(){
 unordered_map<int, int> map;
 hm[0]=0;
 hm[1]=1;
 hm[2]=2;
 for (auto h : map)
 {
   cout << h.first << "->" << h.second << endl;
 }
}
int main()
{
	test1();//键值为string验证
	test2();//键值为int验证
}

但是什么时候我们才要进行hash函数与比较函数的重写呢?在键值为自定义数据类型的时候,或者当键值string类型时,需要对其进行一些操作时。

//自定义数据类型作为键值:
struct Node
{
 Node(int data, string letter){ this->data = data; this->letter = letter; }
 int data;
 string letter;
 bool operator==(const Node & p) const
 {
  return data == p.data && letter == p.letter;
 }
};
struct hash_name
{
 size_t operator()(const Node & p) const{
  return hash<string>()(p.letter) ^ hash<int>()(p.data);
 }
};
int main(void)
{
 Node n1(1, "a");
 Node n2(2, "b");
 Node n3(3, "c");
 Node n4(4, "d");
 unordered_map<Node, int, hash_name>map;
 map[n1] = 1;
 map[n2] = 2;
 map[n3] = 3;
 map[n4] = 4;
 for (auto h : map){
  cout << h.first.letter << "->" << h.second << endl;
 }
}

unordered_map相关函数与hash_map相关函数用法差不多。在我参考的文章里面有。这下以后就可以顺利的使用哈希了。以后要是新学了再继续补充。

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/227424
推荐阅读
相关标签
  

闽ICP备14008679号