哈希表

本文轉載自http://blog.chinaunix.net/uid-24951403-id-2212565.html

哈希表是種數據結構,它可以提供快速的插入操作和查找操作。第一次接觸哈希表時,它的優點多得讓人難以置信。不論哈希表中有多少數據,插入和刪除(有時包括側除)只需要接近常量的時間即0(1)的時間級。實際上,這只需要幾條機器指令。

對哈希表的使用者一一人來說,這是一瞬間的事。哈希表運算得非常快,在計算機程序中,如果需要在一秒種內查找上千條記錄通常使用哈希表(例如拼寫檢查器)哈希表的速度明顯比樹快,樹的操作通常需要O(N)的時間級。哈希表不僅速度快,編程實現也相對容易。

哈希表也有一些缺點它是基與數組的,數組創建後難於擴展某些哈希表被基本填滿時,性能下降得非常嚴重,所以程序雖必須要清楚表中將要存儲多少數據(或者準備好定期地把數據轉移到更大的哈希表中,這是個費時的過程)。

而且,也沒有一種簡便的方法可以以任何一種順序〔例如從小到大)遍歷表中的數據項。如果需要這種能力,就只能選擇其他數據結構。

然而如果不需要有序遍歷數據,井且可以提前預測數據量的大小。那麼哈希表在速度和易用性方面是無與倫比的。

散列函數能使對一個數據序列的訪問過程更加迅速有效,通過散列函數,數據元素將被更快地定位:

  1. 直接尋址法:取關鍵字或關鍵字的某個線性函數值爲散列地址。即H(key)=key或H(key) = a•key + b,其中a和b爲常數(這種散列函數叫做自身函數)

  2. 數字分析法

  3. 平方取中法

  4. 摺疊法

  5. 隨機數法

  6. 除留餘數法:取關鍵字被某個不大於散列表表長m的數p除後所得的餘數爲散列地址。即 H(key) = key MOD p, p<=m。不僅可以對關鍵字直接取模,也可在摺疊、平方取中等運算之後取模。對p的選擇很重要,一般取素數或m,若p選的不好,容易產生同義詞。

1. 開放尋址法:Hi=(H(key) + di) MOD m, i=1,2,…, k(k<=m-1),其中H(key)爲散列函數,m爲散列表長,di爲增量序列,可有下列三種取法:

  1. di=1,2,3,…, m-1,稱線性探測再散列;

  2. di=1^2, (-1)^2, 2^2,(-2)^2, (3)^2, …, ±(k)^2,(k<=m/2)稱二次探測再散列;

  3. di=僞隨機數序列,稱僞隨機探測再散列。 ==

  2. 再散列法:Hi=RHi(key), i=1,2,…,k RHi均是不同的散列函數,即在同義詞產生地址衝突時計算另一個散列函數地址,直到衝突不再發生,這種方法不易產生“聚集”,但增加了計算時間。

  3. 鏈地址法(拉鍊法)

當存儲結構是鏈表時,多采用拉鍊法,用拉鍊法處理衝突的辦法是:把具有相同散列地址的關鍵字(同義詞)值放在同一個單鏈表中,稱爲同義詞鏈表。有m個散列地址就有m個鏈表,同時用指針數組T[0..m-1]存放各個鏈表的頭指針,凡是散列地址爲i的記錄都以結點方式插入到以T[i]爲指針的單鏈表中。T中各分量的初值應爲空指針。
  例如,按上面例9.4所給的關鍵字序列,用拉鍊法構造散列表如圖9.14所示。 


  
  用拉鍊法處理衝突,雖然比開放定址法多佔用一些存儲空間用做鏈接指針,但它可以減少在插入和查找過程中同關鍵字平均比較次數(平均查找長度),這是因爲,在拉鍊法中待比較的結點都是同義詞結點,而在開放定址法中,待比較的結點不僅包含有同義詞結點,而且包含有非同義詞結點,往往非同義詞結點比同義詞結點還要多。
  如前面介紹的例9.4中,用線性探測法構造散列表的過程,我們知道,對前5個關鍵字的查找,每一個僅需要比較一次,對關鍵字49和24的查找,則需要比較2次,對關鍵字38的查找則需要比較4次,而對43的查找則需要比較3次。因此,對用線性探測法構造的散列表的平均查找長度爲:
    ASL=(1×5+2×2+3×1+4×1)/9 ≈1.78
而用拉鍊法構造的散列表上查找成功的平均查找長度爲:
    ASL=(1×5+2×3+3×1)/9≈1.55
顯然,開放定址法處理衝突的的平均查找長度要高於拉鍊法處理衝突的平均查找長度。但它們都比前面介紹的其它查找方法的平均查找長度要短。

 

 


#define HASHSIZE 32    


//待存入表格數據

char *keywords[] = {
        "auto", "break", "case", "char", "const", "continue", "default", 
        "do",
        "double", "else", "enum", "extern", "float", "for", "goto", 
        "if",
        "int", "long", "register", "return", "short", "signed", "sizeof", 
        "static",
        "struct", "switch", "typedef", "union", "unsigned", "void", "volatile",
        "while"
};

char keybuf[HASHSIZE][10];
static char val_flag[HASHSIZE];
//標緻已佔用存儲單元


void ClearFlag()
{
    int i;
    
    for (= 0;< HASHSIZE;i++)
    {
        val_flag[i] = (HASHSIZE+1);
//清標緻位

    }
}

//哈希函數,從數據中抽出某個成員用於哈希值的計算

unsigned int hash(char *s)
{
    unsigned int hashval;
    int i = 0;

    for (hashval = 0; *!= '\0'; s++)
        hashval = *+ 31 * hashval;
    hashval = hashval % HASHSIZE; 
//計算下標


    while ((val_flag[hashval] != (HASHSIZE+1)) && (i<32))
    {
        i++;
        hashval = (hashval + i)%HASHSIZE;    
//衝突處理,存儲單元(下標)偏移

    }
    if (i<HASHSIZE)
    {
        printf("\n元素下標(%d): 衝突次數: %d -- ",hashval,i);
        val_flag[hashval] = hashval; 
//表示該單元被佔用

        return hashval;
    }
    return -1;
}

int main(void) 
{
  int i, size, pos;

  size = sizeof(keywords) / sizeof(keywords[0]);
//計算關鍵字數量

 
  
//將數據存入哈希表

  ClearFlag(); 
  for(= 0;< size; i++)
     strcpy(keybuf[hash(keywords[i])],keywords[i]);

  
//根據數據結構中某個成員作爲索引值,查找對應數據

  ClearFlag(); 
  for(= 0; i < size; i++)
  {
    pos = hash(keywords[i]);    
    printf("%-10s: %-3d\n", keybuf[pos], pos);
  }

  return 0;
}


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