基礎算法和數據結構合集:
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++;
}
雖然圖示中是每個哈希值都鏈接了一個單鏈表,但是實際存儲的時候並不需要開闢那麼多單鏈表,因爲也不可能開那麼多數組,我們只需要開一個能夠存儲所有數據的數組就行了。
開放尋址法
在拉鍊法中我們是用了單鏈表來解決衝突,那麼不用單鏈表怎麼解決衝突呢。很簡單,我們插入一個元素的時候,如果它的哈希值對應的位置已經有人了,也就是衝突了,我們就往後看,直到找到一個空的位置。
這裏不再給出開放尋址法的代碼了,可自行查閱相關資料。