哈希表和unordered_map和unordered_set
哈希表和unordered_map和unordered_set
哈希表的定义
(hash table)又称散列表,是根据关键字进行访问的数据结构
1)哈希表建立了一种关键字和存储地址之间的映射关系,使每个关键字与结构体中的唯一存储位置相对应
2)在理想情况下,在散列表中进行查找的时间复杂度为O(1),即与表中的元素数量无关
哈希函数
将关键字映射成对应地址的函数就是哈希函数,记作:hash(key)=addr
哈希冲突
哈希函数可能会把两个或者两个以上不同的关键字,映射到同一位置,这种情况称为哈希冲突,起冲突的两个不同关键字称为同义词
哈希冲突是不可避免的,我们需要设计优秀的哈希函数去处理而不是去消除
常⻅的哈希函数
直接定址法
直接取关键字的某个线性函数值为散列地址,散列函数是 hash(key) = key 或 hash(key)= a× key + b其中 a 与 b 为常数。这种⽅式计算⽐较简单,适合关键字的分布基本连续的情况,但是若关键字分布不连续,空位较多,则会造成存储空间的浪费。
除留余数法
除留余数法,顾名思义,假设哈希表的⼤⼩为 M,那么通过 key 除以 M 的余数作为映射位置的下标,
也就是哈希函数为:hash(key) = key % M。因此,这种⽅法的重点就是选好模数 M。
• 建议 M 取不太接近 2 的整数次冥的⼀个质数(素数)。
处理哈希冲突
有时候哈希表⽆论选择什么哈希函数都⽆法避免冲突,那么插⼊数据时,如何解决冲突呢?主要有两种⽅法,线性探测法和链地址法
线性探测法
从发⽣冲突的位置开始,依次线性向后探测,直到寻找到下⼀个没有存储数据的位置为⽌,如果⾛到哈希表尾,则回绕到哈希表头的位置。
链地址法
链地址法中所有的数据不再直接存储在哈希表中,哈希表中存储⼀个指针,没有数据映射这个位置时,这个指针为空,有多个数据映射到这个位置时,我们把这些冲突的数据链接成⼀个链表,挂在哈希表这个位置下⾯。
代码如下:
#include <iostream>
#include <cstring>using namespace std;const int N=23,INF=0x3f3f3f3f;int h[N];//初始化
void Init()
{memset(h,0x3f,sizeof h);
}//哈希函数 - 计算出X的取值位置
int f(int x)
{int id=(x%N+N)%N;//处理哈希函数while(h[id]!=INF&&h[id]!=h[x]) {id++;if(id==N) id=0;}return id;
}
//插入元素
void insert(int x)
{int id=f(x);h[id]=x;
}//查找元素
bool find(int x)
{int id=f(x);return h[id]==x;
}
int main()
{Init();int n; cin >> n;while(n--){int op, x; cin >> op >> x;if(op == 1) // 插?{insert(x);}else // 查询{if(find(x)) cout << "yes" << endl;else cout << "no" << endl;}}return 0;
}
unordered_set / unordered_multiset
set 与 unordered_set 的区别就是,前者是⽤红⿊树实现的,后者是⽤哈希表实现的。使⽤的⽅式是完全⼀样的。⽆⾮就是存储和查找的效率不⼀样,以及前者存的是有序的,后者⽆序
