哈希表理论基础
哈希表
哈希表、散列表都是HashTable。
哈希表是根据关键码的值而直接进行访问的数据结构。
一般哈希表都是用来快速判断一个元素是否出现集合里。
哈希函数
在下图中,通过一个哈希函数将名字转化为哈希表的索引数字。转化时为了保证索引数字小于哈希表的大小,所以进行取模。
哈希冲突
如果学生数量大于哈希表的长度,此时就算哈希函数计算的再均匀,也避免不了会有几位学生的名字同时映射到哈希表同一个索引下标的位置。
哈希冲突:
一般哈希碰撞有三种解决方法: 拉链法、线性探测法、再哈希法。
拉链法:
将所有哈希地址相同的记录都链接在同一链表中。
线性探测法
线性探测法一定要保证tableSize大于dataSize。我们需要依靠哈希表中的空位来解决冲突问题。当我们的所需要存放值的位置被占了,我们就往后面一直加1并对m取模直到存在一个空余的地址供我们存放值,取模是为了保证找到的位置在0~m-1的有效空间之中。
再哈希法
同时构造多个不同的哈希函数,等发生哈希冲突时就使用第二个、第三个……等其他的哈希函数计算地址,直到不发生冲突为止。虽然不易发生聚集,但是增加了计算时间。
常见的三种哈希结构
当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。
- 数组
- set
- map
map理论基础
map提供三种数据结构:
std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。std::map 和std::multimap 的key是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。
map结构图(来自网络):
使用map前需要包含头文件:#include < map >
需要使用std命名空间:using namespace std;
map初始化:
map<Type_key, Type_value> mp;
map<int, int> mp_int;//示例:声明一个内部键值对为<int, int>的map
//一个key只对应一个value,一个value可能对应多个key
map的基本操作:
方法 | 作用 |
---|---|
[key] | 获取该键对应的值,如果键不存在,则返回空type。例如:如果map类型是map< int , vector< int > >则返回空vector,如果map类型是map< int , int >则返回0。 |
insert(pair<Type, Type>(key, value)) | 插入一个键值对 |
erase(key) | 删除指定的键值对 |
find(key) | 查找指定的键值对的迭代器,找不到时则返回map.end() |
count(key) | 查找该键的数量,只返回0或1 |
size() | 返回键值对的数量 |
clear() | 清空所有键值对 |
empty() | 判断map是否为空 |
begin() | 返回第一个键值对的迭代器 |
end() | 返回最后一个键值对之后的迭代器 |
map的遍历:
map<int, int> mp;
for (auto &i : mp)
{
cout<<i.first<<' '<<i.second<<endl;
}
set理论基础
set提供三种数据结构:
std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。
当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。
set结构图
每个元素只会在set(集合)中出现一次。set中的元素是按照一定的顺序进行存储和访问的。如果需要无序set可以使用unordered_set。
使用set前需要包含头文件:#include < set > 或#include < unordered_set >
需要使用std命名空间:using namespace std;
set初始化:
set<Type> st;
set<Type> st(arr.begin(),arr.end());//将vector转化为set
set<Type> set1(set2);//第一种拷贝构造方法
set<Type> set2(set1.begin(),set1.end());//第二种拷贝构造方法
unordered_set<Type> st;
set的基本操作:
方法 | 作用 |
---|---|
insert(x) | 插入一个元素 |
erase(x) | 删除指定的元素,可以传入一个迭代器或一个值 |
find(x) | 查找指定的元素的迭代器,找不到时则返回set.end() |
count(key) | 查找该元素的数量,只返回0或1 |
size() | 返回元素的数量 |
clear() | 清空所有元素 |
empty() | 判断set是否为空 |
begin() | 返回第一个元素的迭代器 |
end() | 返回最后一个元素之后的迭代器 |
set的遍历:
for (auto &i : set)
{
cout<< i <<endl;
}