存储结构

  1. 开放寻址法(又名“蹲坑法”)
  2. 拉链法

操作类型

  1. 添加
  2. 查找
  3. 删除(很少有,而且删除不是严格意义上的删除,只是打一个标记)

作用

将大的数据范围映射到小的值域

如何实现

  1. 哈希函数,一般用取模运算
  2. 冲突处理
    1. 拉链法
    2. 开放寻址法
      1. 线性探查法(即当前坑位被占,向后挪一位继续探测)
      2. 二次探查法(即当前坑位被占,向后挪 i*i 位,注意取模,i从0开始递增)

840. 模拟散列表

维护一个集合,支持如下几种操作:

  1. I x,插入一个数 x;
  2. Q x,询问数 x 是否在集合中出现过;

现在要进行 N 次操作,对于每个询问操作输出对应的结果。
输入格式
第一行包含整数 N,表示操作数量。
接下来 N行,每行包含一个操作指令,操作指令为 I xQ x 中的一种。
输出格式
对于每个询问指令 Q x,输出一个询问结果,如果 x 在集合中出现过,则输出 Yes,否则输出 No
每个结果占一行。
数据范围
1≤N≤105
−109≤x≤109
输入样例:

  1. 5
  2. I 1
  3. I 2
  4. I 3
  5. Q 2
  6. Q 5

输出样例:

  1. Yes
  2. No

拉链法

插入:用一个数组存储链表的头指针,根据元素的哈希值找到对应位置的链表,然后采用头插法插入即可。
查询:查询时也是一样的哈希操作,找到该元素对应的应该被存储的链表,遍历链表找该节点,存在返回true,不存在返回false
数组的长度与具体问题有关。一般来讲输入的规模与数组的长度差不多
这里数组长度取质数

  1. import java.util.*;
  2. import java.io.*;
  3. public class Main {
  4. static final int N = 100003;
  5. static ListNode[] list = new ListNode[N];
  6. public static void main(String[] sss) throws IOException{
  7. BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  8. BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
  9. int n = Integer.parseInt(br.readLine());
  10. while (n-- > 0) {
  11. String[] str = br.readLine().split(" ");
  12. int x = Integer.parseInt(str[1]);
  13. if (str[0].equals("I")) insert(x);
  14. else {
  15. if (query(x)) bw.write("Yes\n");
  16. else bw.write("No\n");
  17. }
  18. }
  19. br.close();
  20. bw.close();
  21. }
  22. static void insert(int x) {
  23. //找到应该插入的链表
  24. int k = ((x % N) + N) % N;
  25. if (list[k] == null) {
  26. list[k] = new ListNode(x);
  27. }
  28. else {
  29. ListNode node = new ListNode(x, list[k].next);
  30. list[k].next = node;
  31. }
  32. }
  33. static boolean query(int x) {
  34. int k = ((x % N) + N) % N;
  35. ListNode cur = list[k];
  36. while (cur != null) {
  37. if (cur.val == x) return true;
  38. cur = cur.next;
  39. }
  40. return false;
  41. }
  42. }
  43. class ListNode {
  44. int val;
  45. ListNode next;
  46. ListNode(int x) {
  47. this.val = x;
  48. }
  49. ListNode(int x, ListNode next) {
  50. this.val = x;
  51. this.next = next;
  52. }
  53. }

蹲坑法

开辟一个输入规模两到三倍的数组存储输入元素,用一个输入中不存在的元素NONE作为空的记录
插入和查找用一个函数 find() 完成,find返回数组的下标
将元素哈希,找到对应坑位,如果该坑已经有元素,继续向后寻找直至找到一个空位置或者某个坑位的元素值与该元素相同,返回对应的数组下标
如果是插入:h[find(x)] = x
如果是查询:h[find(x)] == NONE,如果相等说明该元素不存在。

  1. import java.util.*;
  2. import java.io.*;
  3. public class Main {
  4. static final int N = 200003, NONE = 0x3f3f3f3f;
  5. static int[] h = new int[N];
  6. static {
  7. //用NONE初始化h
  8. Arrays.fill(h, NONE);
  9. }
  10. public static void main(String[] sss) throws IOException{
  11. BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  12. BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
  13. int n = Integer.parseInt(br.readLine());
  14. while (n-- > 0) {
  15. String[] str = br.readLine().split(" ");
  16. int x = Integer.parseInt(str[1]);
  17. if (str[0].equals("I")) h[find(x)] = x;
  18. else {
  19. if (h[find(x)] == NONE) bw.write("No\n");
  20. else bw.write("Yes\n");
  21. }
  22. }
  23. br.close();
  24. bw.close();
  25. }
  26. //线性探查法
  27. static int find(int x) {
  28. int k = ((x % N) + N) % N;
  29. for (int i = k; ; i++) {
  30. if (i == N) i = 0;
  31. if (h[i] == NONE || h[i] == x) return i;
  32. }
  33. }
  34. }

开放地址法解决冲突采用二次探测法的例题:
1564. 哈希