C++笔记:std::hash

cpp 复制代码
#include<functional>
template< class Key >
struct hash; 

std::hash 本质上是一个 模板函数对象(function object)

可以把它理解为一个函数对象,自然它实现了operator()方法。

本文不深究具体原理,只讲一下如何使用std::hash

如何使用

cpp 复制代码
#include <iostream>
#include <functional>

int main()
{
    std::hash<int> hasher;

    size_t h1 = hasher(10);
    size_t h2 = hasher(20);

    std::cout << h1 << std::endl;
    std::cout << h2 << std::endl;
}
bash 复制代码
10155668643424727455
14764355265743743681

返回值类型是std::size_t

标准库的规则

  1. 接收 Key 类型的单个参数
  2. 返回表示参数散列值的 std::size_t 类型。
  3. 调用时不抛出异常。
  4. 对于二个相等的参数 k1k2 , std::hash<Key>()(k1) == std::hash<Key>()(k2) 。
  5. 对于二个相异而不相等的参数 k1k2 , std::hash<Key>()(k1) == std::hash<Key>()(k2) 的概率应非常小,接近 1.0/std::numeric_limits<std::size_t>::max() 。

标准库已经提供了很多类型的哈希实现:

bash 复制代码
//基本类型
int
long
float
double
bool
//指针
T*
//字符串
std::string
std::wstring
std::string_view    //C++20支持
//注意对于C字符串const char *,并不会对所有字符进行哈希,而是仅视为指针

std::hash 在 unordered_*里的作用

无序关联容器 std::unordered_set 、 std::unordered_multiset 、 std::unordered_map 、 std::unordered_multimap 以该模板 std::hash 的特化为默认散列函数。

cpp 复制代码
template <class _Kty, class _Ty, class _Hasher = std::hash<_Kty>, class _Keyeq = std::equal_to<_Kty>
class unordered_map;

如何给自定义的类定义hash?

假设我们自定义了一个类Point

cpp 复制代码
struct Point
{
    int x;
    int y;
};
std::unordered_map<Point, int> mp; // ❌ 编译错误

因为std::hash<Point>不存在

哈希表必须知道:Point -> hash

方法1:特化 std::hash(最常用)

cpp 复制代码
#include <iostream>
#include <functional>
#include<unordered_map>
struct Point {
    int x;
    int y;
};
// std::hash 的自定义特化能注入 namespace std
namespace std
{
    template<>
    struct hash<Point>
    {
        size_t operator()(const Point& p) const
        {
            return std::hash<int>{}(p.x) ^ std::hash<int>{}(p.y);
        }
    };
}
int main()
{
    std::hash<Point> hasher;//可以
    std::unordered_map<Point, int> mp;//可以实例化
    mp.insert(std::make_pair(Point{ 1,2 }, 1));//但插入有问题
    
}

哈希表不仅要 hash,还要判断相等。

cpp 复制代码
#include <iostream>
#include <functional>
#include<unordered_map>
struct Point
{
    int x;
    int y;
    //要加入==操作
    bool operator==(const Point& other) const
    {
        return x == other.x && y == other.y;
    }
};
// std::hash 的自定义特化能注入 namespace std
namespace std
{
    template<>
    struct hash<Point>
    {
        size_t operator()(const Point& p) const
        {
            return std::hash<int>{}(p.x) ^ std::hash<int>{}(p.y);
        }
    };
}
int main()
{
    std::hash<Point> hasher;
    std::unordered_map<Point, int> mp;
    mp.insert(std::make_pair(Point{ 1,2 }, 1));
    
}

方法2:自定义 hash 函数(不用特化)

cpp 复制代码
#include <iostream>
#include <functional>
#include<unordered_map>
struct Point
{
    int x;
    int y;

    bool operator==(const Point& other) const
    {
        return x == other.x && y == other.y;
    }
};
//相当于自己设计hash算法的可调用对象类
struct PointHash
{
    std::size_t operator()(const Point& p) const
    {
        return p.x * 31 + p.y;
    }
};
int main()
{
    //让哈希表用自己的算法
    std::unordered_map<Point, int,PointHash> mp;
    mp.insert(std::make_pair(Point{ 1,2 }, 1));
    
}
相关推荐
小灰灰搞电子9 小时前
C++ 引用折叠详解
c++
知产xiao_xin9 小时前
知识产权入门 —— 版权
经验分享·笔记·知识产权
呆萌很10 小时前
英语 1‑12 月单词联想记忆
笔记
布莱克60510 小时前
数组详解:定义、作用、应用场景及与链表的区别(C/C++ 代码讲解)
c语言·开发语言·c++·数组
牛油果子哥q11 小时前
C++内存池与对象池精讲:内存碎片、自定义分配器、对象池实现、STL allocator原理、工程落地与性能对比
java·开发语言·c++
万法若空11 小时前
C/C++ 类型别名定义方式对比
java·c语言·c++
金立基包装胶水11 小时前
纸袋热封频繁不良,先排查胶料这一堆问题
大数据·笔记·其他
常驻客栈12 小时前
B-深入理解计算机系统第3版之第11章:网络编程
开发语言·笔记·学习笔记·常驻客栈·深入理解计算机系统
血小板要健康12 小时前
网格 dfs 与 FloodFill:从岛屿、区域到搜索路径
笔记·算法·leetcode·深度优先
NoteDeep 深度笔记12 小时前
Observer: 思维 ≠ 自己
笔记