哈希表

基礎算法和數據結構合集:
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++;
    }

雖然圖示中是每個哈希值都鏈接了一個單鏈表,但是實際存儲的時候並不需要開闢那麼多單鏈表,因爲也不可能開那麼多數組,我們只需要開一個能夠存儲所有數據的數組就行了。

開放尋址法

在拉鍊法中我們是用了單鏈表來解決衝突,那麼不用單鏈表怎麼解決衝突呢。很簡單,我們插入一個元素的時候,如果它的哈希值對應的位置已經有人了,也就是衝突了,我們就往後看,直到找到一個空的位置。
在這裏插入圖片描述
這裏不再給出開放尋址法的代碼了,可自行查閱相關資料。

發佈了70 篇原創文章 · 獲贊 237 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章