本章我们学习了所有的无序关联式容器:unordered_map容器、unordered_multimap容器、unordered_set容器、unordered_multiset容器,他们都是底层基于哈希表的结构封装起来的一个类。在存储基本数据类型时,键(key)能够通过哈希函数转为哈希值(数据类型为size_t),当我们的键为自定义数据类型时,就需要我们重新定义哈希函数和重载operator'=='运算符。哈希函数一般是通过仿函数来实现的。仿函数重写哈希函数时,要求我们的哈希函数尽量让键均匀分布、键值保持唯一性和方便计算,一般是通过“”位移异或组合法“”来合成哈希值的。下面让我们以unordered_map容器为例,通过它来存放键(key)==user的结构体,重点分析如何重载“==”和重写哈希函数。

读者可想象有这么一个情景:我们有一个用户类(user),成员变量包括姓名(m_s)、年龄(m_age)和性别(m_gender),每个用户对应一个身份码id,程序要求我们快速根据用户来找到对应id,我们就可以充分利用unordered_map容器来满足这个需求。

#include<iostream>
#include<string>
#include<unordered_map>
/*自定义哈希函数*/
using namespace std;
struct user
{
bool operator==(const user&u)const//const不能丢 
{
return m_s==u.m_s&&m_age==u.m_age&&m_gender==u.m_gender;
}
user(string s,int age,string gender):m_s(s),m_age(age),m_gender(gender){};
string m_s;
int m_age;
string m_gender;
};
struct fhash
{
size_t operator()(const user&u)const
{
/*组合所有字段的哈希值,让每个成员变量参与分配*/
size_t h1 = hash<string>{}(u.m_s);
size_t h2 = hash<int>{}(u.m_age);
size_t h3 = hash<string>{}(u.m_gender);
/*经典操作*/ 
/*通过'<<'位移和'^'按位异或,高效返回尽量“独立 ”的哈希值,避免哈希冲突*/
return  h1^(h2<<1)^(h3<<2);
        
}
};
void test()
{
unordered_map<user,int,fhash> ump;
ump.insert({{"dot01",18,"男"},1}); 
ump.insert({{"dot02",18,"男"},2});
ump.insert({{"dot03",18,"女"},3});
ump.insert({{"dot04",18,"男"},4});
ump.insert({{"dot05",18,"女"},5});
/*遍历我们的unordered_map容器*/
cout << "遍历容器内所有用户:\n" ;
for(auto it = ump.begin();it!=ump.end();++it)
{
	cout<< "用户名:【"  << it->first.m_s << "】用户年龄: 【"  << it->first.m_age << "】用户性别:【"  << it->first.m_gender << "】用户识别码:【"  << it->second << "】\n";
}
}
int main(){
    test();
    return 0;
}

编译结果如下:

重载operator‘==’和自定义哈希函数

总结:对于无序关联式容器,当键为自定义数据类型时,就需要我们重新定义哈希函数和重载operator'=='运算符,运算符重载不难,但是需要记住位移异或组合法来合成哈希值。

点赞(0)

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

Dotcpp在线编译      (登录可减少运行等待时间)