Hash(哈希)是一种将输入数据映射到固定大小值的技术,广泛应用于数据结构和算法中。在 C++ 中,Hash 函数和相关的数据结构,如哈希表,能够显著提升程序的效率。本文将探讨 Hash 的基本概念及其在 C++ 中的应用。
Hash 函数的基本概念
Hash 函数是一个将输入(通常是数据)映射到固定大小的哈希码的函数。其基本性质包括:
- 确定性:对于相同的输入,哈希函数应该总是产生相同的输出。
- 均匀分布:哈希码应该均匀地分布在可能的值范围内,以减少冲突的概率。
- 快速计算:哈希函数的计算应该足够快速,以满足实时需求。
哈希表(Hash Table)
哈希表是一种使用哈希函数来快速查找数据的数据结构。其基本实现包括以下几部分:
- 哈希函数:将键映射到数组索引。
- 桶(Buckets):数组中存储数据的槽位。
- 处理冲突的方法:如链地址法(链表)、开放定址法(线性探测或二次探测)。
C++ 标准库中提供了 std::unordered_map
和 std::unordered_set
,这些容器基于哈希表实现。下面我们将介绍如何在 C++ 中使用这些容器,以及如何自定义哈希函数。
使用 std::unordered_map
set&unordered_setset区别
std::unordered_map
是一个基于哈希表的关联容器,它存储键值对,并提供快速的查找、插入和删除操作。
#include <iostream>
#include <unordered_map>
int main() {
// 创建一个 unordered_map,键是字符串,值是整数
std::unordered_map<std::string, int> myMap;
// 插入键值对
myMap["apple"] = 3;
myMap["banana"] = 2;
myMap["cherry"] = 5;
// 查找并输出值
std::cout << "apple: " << myMap["apple"] << std::endl;
std::cout << "banana: " << myMap["banana"] << std::endl;
// 遍历 unordered_map
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
自定义哈希函数
有时,我们需要为自定义类型提供自定义哈希函数。可以通过特化 std::hash
模板来实现。
#include <iostream>
#include <unordered_map>
#include <string>
// 自定义类型
struct Person {
std::string name;
int age;
bool operator==(const Person& other) const {
return name == other.name && age == other.age;
}
};
// 自定义哈希函数
namespace std {
template <>
struct hash<Person> {
size_t operator()(const Person& p) const {
// 使用 std::hash 处理基本类型,并组合结果
return hash<std::string>()(p.name) ^ (hash<int>()(p.age) << 1);
}
};
}
int main() {
// 使用自定义哈希函数的 unordered_map
std::unordered_map<Person, std::string> peopleMap;
// 插入键值对
peopleMap[{ "Alice", 30 }] = "Engineer";
peopleMap[{ "Bob", 25 }] = "Artist";
// 查找并输出值
for (const auto& pair : peopleMap) {
std::cout << pair.first.name << " (" << pair.first.age << "): " << pair.second << std::endl;
}
return 0;
}
处理冲突
在哈希表中,冲突是不可避免的。当多个键被映射到相同的哈希码时,我们需要处理这些冲突。常见的冲突解决方法包括:
- 链地址法:使用链表存储所有具有相同哈希码的元素。
- 开放定址法:寻找另一个空槽位存储冲突的元素。