1. 程式人生 > >雜湊表Hash:概念與基本操作

雜湊表Hash:概念與基本操作

什麼是Hash

Hash就像是一個桶排,那隻不過是把各個元素的數值當做下標進行儲存.其最常用的用途就是用來判重.但是,如何對字串進行判重,不可能一個一個往前超,若n上萬則顯然不可行.我們可以選擇進行Hash,將每一個字串或者大數字進行一定的操作即可進行.


對大整數型別進行Hash

取模法

對於每一個大整數進行取模,即除以一個大質數(例如107,10007,1000007,1-奇數個0-7),這樣就作為陣列的下標進行儲存了.

為什麼要對一個大整數取模

emmmmmm......經眾多數學家證明重複的機率較小

萬一實在有不可避免的誤差/出題人卡你怎麼辦

有兩種方法:
1.線性探測開拓地址法
2.拉鍊法


對字串進行Hash

對字串,我們可以選擇按權展開法進行實現.
例如:對於字元abc,我們可以轉換成26進位制,即:126^0+226^1+3*26^2,很好理解吧
例如,對於字串3gT,我們可以轉換成某個質數進位制即可.
但是,不要忘了,在按權展開的時候需要對一個大整數取模

如何避免字串Hash的誤差

同樣:
1.線性探測開拓地址法
2.拉鍊法


線性探測開拓地址法

若hash[k]已經被填入,則嘗試填寫hash[k+1],hash[k+2]....等陣列.最後在查詢的時候也逐一查詢即可.但畢竟查詢不方便,個人更加偏向於拉鍊發


拉鍊法

跟據每一個數值的下標擴充一個連結串列,每次在連結串列上逐一查詢即可.
為了方便實現,我們可以選用STL中的vector(即動態陣列或不定長陣列)來代替連結串列,這樣也十分容易實現.


整數Hash例項:不重複數字【JLOI2011】

題目描述
給出N個數,要求把其中重複的去掉,只保留第一次出現的數。
例如,給出的數為1 2 18 3 3 19 2 3 6 5 4,其中2和3有重複,去除後的結果為1 2 18 3 19 6 5 4。
輸入輸出格式
輸入格式:
輸入第一行為正整數T,表示有T組資料。
接下來每組資料包括兩行,第一行為正整數N,表示有N個數。第二行為要去重的N個正整數。
輸出格式:
對於每組資料,輸出一行,為去重後剩下的數字,數字之間用一個空格隔開。
原題體面戳這裡
對於這道題,直接除以大質數1000007即可.(資料水,沒有判重AC了)
程式碼如下:

#include<bits/stdc++.h>
using namespace std;
int Hash[1000009];
inline int h(int x){return x%1000007;}
void work()
{
    memset(Hash,0,sizeof(Hash));
    int n;
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        if (Hash[h(x)]==1) continue;
    }
    printf("\n");
}
int main()
{
    int T;
    scanf("%d",&T);
    while (T--) work();
}

字串Hash

原題題面戳這裡
這道題,我們採用301當做位權;我們採用大質數1000007來取模;用拉鍊法來處理重複;同時用vector來代替連結串列;程式碼實現難度不大.
程式碼如下:

#include<bits/stdc++.h>
using namespace std;
int n,ans=0;
int const M=1000007;//最後取模 
vector<string>Link[1000009];
inline int hash(string x)
{
    int sum=0;int w=307;//累加的值,位權 
    for (int i=0;i<x.length();i++)
        sum=(sum*w+int(x[i]))%M;
    return sum; 
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for (int i=1;i<=n;i++)
    {
        string s;cin>>s;
        int k=hash(s),flag=1;
        for (int j=0;j<Link[k].size();j++) 
            if (s==Link[k][j]) flag=0;
        if (flag==1)
        {
            Link[k].push_back(s);
            ans++;
        }
    }
    cout<<ans;
    return 0;
}