算法-哈希表和相关练习-java
哈希表
哈希表存储方式:1.开放寻址法,2.拉链法
运用:把一个很大的空间映射到比较小的范围(110^9->110^5)
模拟散列表
开放寻址法:直接一个数组模拟蹲坑
1.x mod 10^5(最好质数并且离2的n整次幂尽量远) 2.对于冲突,当前位置有数了就对位置+1直到当前没数为止,一般需要开当前范围的两到三倍,然后定义一个0x3f3f3f是大于10^9的一个数(在最大范围外)
代码:
import java.util.Scanner;
public class Main{static int N = 200003,idx;static int nulls = 0x3f3f3f3f;//最大的数static int[] h = new int[N];public static int find(int x){int k = (x % N + N) % N;while(h[k]!=nulls && h[k] != x){//不为空且没找到就继续找k++;if(k==N) k=0;//到结尾了从头开始}return k;}public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();for(int i = 0; i<N;i++)h[i]=0x3f3f3f3f;while(n -- > 0){String s = scan.next();int x = scan.nextInt();int k = find(x);if(s.equals("I")){ h[k]=x;}else{if(h[k]!=nulls) System.out.println("Yes");else System.out.println("No");}}}
}
拉链法:假设映射到1~10^5内,相当于每个数字的位置都是一个链表,有冲突就插入到当前链表最后面这样
import java.util.Scanner;
public class Main{static int N = 100003,idx;static int[] h = new int[N];static int[] e = new int[N];static int[] ne = new int[N];public static void insert(int x){int k = (x % N + N) % N;//存储下标位置 防止负数e[idx] = x;//赋值ne[idx] = h[k];//该点指向下一个节点h[k] = idx++;//头指向该节点}public static boolean find(int x){int k = (x % N + N) % N;for(int i = h[k];i != -1;i = ne[i]){if(e[i] == x){return true;} }return false;}public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();idx = 0;for(int i = 0; i<N;i++)h[i]=-1;while(n -- > 0){String x = scan.next();if(x.equals("I")){int a = scan.nextInt();insert(a);}else{int b = scan.nextInt();if(find(b)) System.out.println("Yes");else System.out.println("No");}}}
}
字符串哈希

代码
import java.util.Scanner ;
public class Main{//开的是long类型数组,本来是需要进行前缀hash求完之后需要进行模2的64次方来防止相同的冲突,可能超过我们给的数组大小static int N = 100010,P = 131;//p是进制数,惊艳值static long[] h = new long[N];//这是存放hash前缀值得数组static long[] p = new long[N];//这是存放p的n次方的数组public static long get(int l,int r){//这里是将运用了一点前缀和公式的方式进行计算//求l-r区间的hash值,就要用h[r] - h[l-1],因为两者位数不用需要让h[l-1]向左边移到跟h[r]对齐//就比如求1234的3-4区间位,1234 - 12,12左移然后就让12*10^(4-3+1)=12*10^2=1200,然后1234-1200 = 34,这样进行计算出来//然后本题是p进制,所以需要将上面公式中的10换成p就行了//h[0] = 0//h[1] = h[i-1] * P + str[1] = 0*P+a = a//h[2] = a * P + b//h[3] = (a*P+b)*P+c = a*p[2]+b*P+c//h[4] = (a*p[2]+b*P+c)*P+d = a*p[3]+b*p[2]+c*P+d//比如abcd求3-4区间位,就是让h[d]-h[b],h[b]位数不用需要向左移对齐h[d],//h[2]*P^(4-3+1)=(a*P+b)*P^2 = a*P^3+b*P^2//然后就让h[d] - h[b]求出34区间值,(a*p[3]+b*p[2]+c*P+d) - (a*P^3+b*P^2) = c*P+dreturn h[r] - h[l-1]*p[r-l+1];}public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();int m = scan.nextInt();String s = scan.next();p[0] = 1;//这个是p的0次方的值,需要单独写出来,非常重要for(int i = 1 ; i <= n ; i++ ){p[i] = p[i-1] * P;//这里对应每一个下标对应对应P的多少次方h[i] = h[i-1] * P + s.charAt(i-1);//这里是公式,预处理前缀哈希的值,因为是P进制,所以中间乘的是P}while(m -- > 0){int l1 = scan.nextInt();int r1 = scan.nextInt();int l2 = scan.nextInt();int r2 = scan.nextInt();//判断两个区间是不是相同,用get的方法返回值一样说明区间的hash值是一样的if(get(l1,r1) == get(l2,r2)) System.out.println("Yes");else System.out.println("No");}}
}