查找算法之哈希查找

哈希查找定義:

哈希查找是通過計算數據元素的存儲地址進行查找的一種方法。O(1)的查找,即所謂的秒殺。哈希查找的本質是先將數據映射成它的哈希值。哈希查找的核心是構造一個哈希函數,它將原來直觀、整潔的數據映射爲看上去似乎是隨機的一些整數。

哈希查找的操作步驟:

1)       用給定的哈希函數構造哈希表;

2)       根據選擇的衝突處理方法解決地址衝突;

3)       在哈希表的基礎上執行哈希查找。

建立哈希表操作步驟:

1)       step1 取數據元素的關鍵字key,計算其哈希函數值(地址)。若該地址對應的存儲空間還沒有被佔用,則將該元素存入;否則執行step2解決衝突。

2)       step2 根據選擇的衝突處理方法,計算關鍵字key的下一個存儲地址。若下一個存儲地址仍被佔用,則繼續執行step2,直到找到能用的存儲地址爲止。

哈希查找步驟爲:

1)       Step1 對給定key值,計算哈希地址 Di=Hash(key);若HST爲空,則查找失敗;若HST=key,則查找成功;否則,執行step2(處理衝突)。

2)       Step2 重複計算處理衝突的下一個存儲地址 Dk=R(Dk-1),直到HST[Dk]爲空,或HST[Dk]=key爲止。若HST[Dk]=key,則查找成功,否則查找失敗。

 

比如說:”5“是一個要保存的數,然後我丟給哈希函數,哈希函數給我返回一個”2",那麼此時的”5“和“2”就建立一種對應關係,這種關係就是所謂的“哈希關係”,在實際應用中也就形成了”2“是key,”5“是value。

那麼有的朋友就會問如何做哈希,首先做哈希必須要遵守兩點原則:

①: key儘可能的分散,也就是我丟一個“6”和“5”給你,你都返回一個“2”,那麼這樣的哈希函數不盡完美。

②:哈希函數儘可能的簡單,也就是說丟一個“6”給你,你哈希函數要搞1小時才能給我,這樣也是不好的。

其實常用的做哈希的方法有“五種”:

第一種:”直接定址法“。

很容易理解,key=Value+C;這個“C"是常量。Value+C其實就是一個簡單的哈希函數。

第二種:“除法取餘法”。

很容易理解, key=value%C;解釋同上。

第三種:“數字分析法”。

這種蠻有意思,比如有一組value1=112233,value2=112633,value3=119033,

針對這樣的數我們分析數中間兩個數比較波動,其他數不變。那麼我們取key的值就可以是

key1=22,key2=26,key3=90。

第四種:“平方取中法”。此處忽略,見名識意。

第五種:“摺疊法”。

這種蠻有意思,比如value=135790,要求key是2位數的散列值。那麼我們將value變爲13+57+90=160,然後去掉高位“1”,此時key=60,哈哈,這就是他們的哈希關係,這樣做的目的就是key與每一位value都相關,來做到“散列地址”儘可能分散的目地。

影響哈希查找效率的一個重要因素是哈希函數本身。當兩個不同的數據元素的哈希值相同時,就會發生衝突。爲減少發生衝突的可能性,哈希函數應該將數據儘可能分散地映射到哈希表的每一個表項中。

常用的解決衝突的方法有以下兩種:  

(1)   開放地址法  

如果兩個數據元素的哈希值相同,則在哈希表中爲後插入的數據元素另外選擇一個表項。當程序查找哈希表時,如果沒有在第一個對應的哈希表項中找到符合查找要求的數據元素,程序就會繼續往後查找,直到找到一個符合查找要求的數據元素,或者遇到一個空的表項。  

(2)   鏈地址法

將哈希值相同的數據元素存放在一個鏈表中,在查找哈希表的過程中,當查找到這個鏈表時,必須採用線性查找方法。

 

實現哈希函數爲“除法取餘法”,解決衝突爲“開放地址線性探測法”,代碼如下:

 


public class HashSearch {
 
    public static void main(String[] args) {
        //“除法取餘法”
        int hashLength = 13;
 
        int [] array  = { 13, 29, 27, 28, 26, 30, 38 };
 
        //哈希表長度
        int[] hash = new int[hashLength];
        //創建hash
        for (int i = 0; i < array.length; i++)
        {
            insertHash(hash, hashLength, array[i]);
        }
        
        int result = searchHash(hash,hashLength, 29);
 
        if (result != -1)
            System.out.println("已經在數組中找到,索引位置爲:" + result);
        else
            System.out.println("沒有此原始");
    }
 
    /****
     * Hash表檢索數據
     * 
     * @param hash
     * @param hashLength
     * @param key
     * @return
     */
    public static int searchHash(int[] hash, int hashLength, int key) {
        // 哈希函數
        int hashAddress = key % hashLength;
 
        // 指定hashAdrress對應值存在但不是關鍵值,則用開放尋址法解決
        while (hash[hashAddress] != 0 && hash[hashAddress] != key) {
            hashAddress = (++hashAddress) % hashLength;
        }
 
        // 查找到了開放單元,表示查找失敗
        if (hash[hashAddress] == 0)
            return -1;
        return hashAddress;
 
    }
 
    /***
     * 數據插入Hash表
     * 
     * @param hash 哈希表
     * @param hashLength
     * @param data
     */
    public static void insertHash(int[] hash, int hashLength, int data) {
        // 哈希函數
        int hashAddress = data % hashLength;
 
        // 如果key存在,則說明已經被別人佔用,此時必須解決衝突
        while (hash[hashAddress] != 0) {
            // 用開放尋址法找到
            hashAddress = (++hashAddress) % hashLength;
        }
 
        // 將data存入字典中
        hash[hashAddress] = data;
    }
}


原文:https://blog.csdn.net/xiaoping8411/article/details/7706376 
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章