基础算法和数据结构合集:
https://blog.csdn.net/GD_ONE/article/details/104061907
摘要
本文主要介绍哈希表的定义,并用数组模拟哈希表。
哈希表
哈希表又称为散列表,其原理是每个元素都能通过哈希函数得到一个哈希值,然后该元素在表中存储的下标就是该哈希值,访问的时候也通过这个哈希值进行访问。所以理论上查找的时间复杂度是O(1)。
一般的整数哈希函数:hash(x) = (x%P+P)%P
P一般取大于最大数据量的第一个质数。
举个例子:
有一组数,个数为 8,1 3 5 7 13 20 50 101 ,则P取11, 11是大于8的第一个质数。
然后分别求出这8个数的哈希值。
hash(1) = 1
hash(3) = 3
hash(5) = 5
hash(7) = 7
hash(13) = 2
hash(20) = 9
hash(50) = 4
hash(101) = 2
我们发现以上哈希值有两个是相同的,101和13被放在了同一个位置,这被称为“冲突”,数组的一个位置只能存一个数,所以,我们要解决冲突,解决冲突有两种常用的方式:
链地址法(拉链法)
>将数据存储在哈希值对应的单链表中
如图:
这样就解决了冲突的问题。
那么怎么实现呢, 这里采用数组模拟单链表的方式。
题目: 模拟散列表
代码:
import java.io.*;
import java.util.*;
public class Main{
static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
static final int N = 100003; // 假设数据最多为100000个
static int[] hash = new int[N]; //哈希表表头
static int[] data = new int[N]; // 所有单链表共用一个数组空间
static int[] next = new int[N]; // next指针
static int idx = 1; // static标识的数组是自动初始化为0的所以让
//结点的编号从 1 开始,这样就不需要对hash初始化为-1了,当指针为0时,则表示为空。
public static void Insert(int x){
int k = (x % N + N) % N;
data[idx] = x;
next[idx] = hash[k];
hash[k] = idx;
idx++;
}
public static void find(int x) throws IOException{
int k = (x % N + N) % N;
k = hash[k];
Boolean flag = false;
while(k != 0){
if(data[k] == x){
flag = true;
break;
}
k = next[k];
}
if(flag){
out.write("Yes\n");
}
else out.write("No\n");
out.flush();
}
public static void main(String[] agrs)throws IOException {
int n = Integer.parseInt(in.readLine());
for(int i = 0; i < n; i++){
String s[] = in.readLine().split(" ");
switch(s[0]){
case "I" : Insert(Integer.parseInt(s[1])); break;
case "Q" : find(Integer.parseInt(s[1])); break;
}
}
}
}
在以上的代码中Insert是最重要的,这个函数看懂了也就理解了:
public static void Insert(int x){
int k = (x % N + N) % N; // 得到哈希值
data[idx] = x; // 将x存储在单链表中
next[idx] = hash[k]; // 头插法, 将这个元素指向头指针指向的结点
hash[k] = idx; //然后让头指针指向新插入的结点
idx++;
}
虽然图示中是每个哈希值都链接了一个单链表,但是实际存储的时候并不需要开辟那么多单链表,因为也不可能开那么多数组,我们只需要开一个能够存储所有数据的数组就行了。
开放寻址法
在拉链法中我们是用了单链表来解决冲突,那么不用单链表怎么解决冲突呢。很简单,我们插入一个元素的时候,如果它的哈希值对应的位置已经有人了,也就是冲突了,我们就往后看,直到找到一个空的位置。
这里不再给出开放寻址法的代码了,可自行查阅相关资料。