1. 程式人生 > 其它 >雜湊(hash)-----基礎版

雜湊(hash)-----基礎版

雜湊可以表示為一句話:將元素通過一個函式轉換為整數,使得該整數可以儘量唯一地代表這個元素!

來看一個簡單的問題:給出N個正整數,再給出M個正整數,問M個數中的每個數分別是否在N個數中出現過,其中N,M<=105,且所有正整數均不超過105
例子:N=5,M=3,N個正整數為{8,2,4,7,1},M個正整數為{7,4,9},於是後者中7、4在N個正整數中出現過,而9是沒有出現過。

較為簡單的思路:遍歷所有N個正整數,看是否一個數與所要查詢的正整數x相等。這種方法的時間複雜度為O(NM),當N和M都很大的時候(105),顯然無法承受的!

因此想到用空間換時間。設定一個bool陣列hashTable[100010],其中hashTable[x] == true表示正整數x在N個正整數中出現過,否則,沒有出現過。如此,我們就可以在一開始讀入N個正整數時就可以進行預處理,即當讀入的數為x時,就令hashTable[x] == true(hashTable陣列初始化為false)。這種方法的時間複雜度為O(N+M)!
程式碼:

#include <cstdio>
const int maxn = 100010;
bool hashTable[maxn] = {false};

int main() {
    int n,m,x;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%d",&x);
        hashTable[x] = true;
    }
    for(int i=0;i<m;i++){
        scanf("%d",&x);
        if(hashTable[x] == true)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

如果題目需要M個待查詢的數中每個數在N個數中出現的次數,那麼就可以將hashTable換成int型,然後在輸入N個正整數時進行處理,即每當輸入x時,就令hashTable[x]++,這種方法只需要O(N+M)的時間複雜度輸出每個預查詢的數出現的次數。
上面兩個問題的特點:直接把輸入的數作為陣列的下標來對這個陣列的性質進行統計!(此方法非常實用,多練習掌握)

有關雜湊函式內容以及解決衝突的辦法,都在資料結構學科內,可以找本資料結構書籍進行學習!

字串hash問題

字串hansh是將一個字串S對映為一個整數,使得該整數可以儘可能唯一地代表字串S!

如果字串中出現了大寫、小寫字母,可以把A~Z作為0~25,把a~z作為26~51,這樣就變成了五十二進位制轉換為十進位制的問題。由進位制轉換的結論可知,在進位制轉換的過程中,得到的十進位制肯定是唯一的,因此就可以實現將字串對映為整數的需求(注意:轉換的整數最大為26len

-1,其中len為字串的長度)。
程式碼:

int hashFunc(char S[],int len){
    int id = 0;
    for(int i=0;i<len;i++){
        if(S[i] >= 'A' && S[i] <='Z')
            id = id*52+(S[i] - 'A');
        else if(S[i] >= 'a' && s[i] <= 'z')
            id = id*52+(S[i] - 'a' + 26);
    }
    return id;
}

但有時候字串中間會出現數字,而對於數字,有以下兩種處理方式:

1.和上面的小寫字母的處理方法一樣,在進位制數後面再新增十位就可以,這裡就是增大進位制數至62;
2.有的題目可能是直接確定在字串後面是確定個數的數字,那麼此時的處理方法可以是前面的英文字母按照上述思路轉換為整數,而末尾的數字直接拼接上去就好。舉個例子:對於由三個字元加上末尾是一位數字組成的字串“BCD4”來講,可以先將前面的“BCD”轉換為整數731,然後直接將數字4拼接上去成為7314即可。
程式碼:

int hashFunc(char S[],int len){
    int id = 0;
    for(int i=0;i<len;i++){
        if(S[i] >= 'A' && S[i] <='Z')
            id = id*52+(S[i] - 'A');
        else if(S[i] >= 'a' && s[i] <= 'z')
            id = id*52+(S[i] - 'a' + 26);
        else
            id = id*10+(S[i] - '0');
    }
    return id;
}

習題練習:
給出N個字串(由恰好三個大寫字母拼成),再給出M個查詢字串,問每個查詢字串在N個字串中出現的次數。
程式碼:

#include <cstdio>
const int maxn = 100;
char S[maxn][5],temp[5];
int hashTable[26*26*26+10];

int hashFunc(char S[],int len){
    int id = 0;
    for(int i=0;i<len;i++){
        if(S[i] >= 'A' && S[i] <='Z')
            id = id*52+(S[i] - 'A');
    }
    return id;
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        sacnf("%d",&S[i]);
        int id = hashFunc(S[i],3);
        hashTable[id]++;
    }
    for(int i=0;i<n;i++){
        scanf("%s",temp);
        int id = hashFunc(temp,3);
        printf("%d\n",hashTable[i]):
    }
    return 0;
}

hash進階版: