最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解散列表算法與其相關(guān)的C語(yǔ)言實(shí)現(xiàn)

 更新時(shí)間:2015年08月11日 09:42:55   作者:zinss26914  
這篇文章主要介紹了詳解散列表算法與其相關(guān)的C語(yǔ)言實(shí)現(xiàn),平時(shí)經(jīng)常出現(xiàn)于各大考試競(jìng)賽與程序員面試題目當(dāng)中,需要的朋友可以參考下

散列表(也叫哈希表)是一種查找算法,與鏈表、樹(shù)等算法不同的是,散列表算法在查找時(shí)不需要進(jìn)行一系列和關(guān)鍵字(關(guān)鍵字是數(shù)據(jù)元素中某個(gè)數(shù)據(jù)項(xiàng)的值,用以標(biāo)識(shí)一個(gè)數(shù)據(jù)元素)的比較操作。

    散列表算法希望能盡量做到不經(jīng)過(guò)任何比較,通過(guò)一次存取就能得到所查找的數(shù)據(jù)元素,因而必須要在數(shù)據(jù)元素的存儲(chǔ)位置和它的關(guān)鍵字(可用key表示)之間建立一個(gè)確定的對(duì)應(yīng)關(guān)系,使每個(gè)關(guān)鍵字和散列表中一個(gè)唯一的存儲(chǔ)位置相對(duì)應(yīng)。因此在查找時(shí),只要根據(jù)這個(gè)對(duì)應(yīng)關(guān)系找到給定關(guān)鍵字在散列表中的位置即可。這種對(duì)應(yīng)關(guān)系被稱(chēng)為散列函數(shù)(可用h(key)表示)。

    根據(jù)設(shè)定的散列函數(shù)h(key)和處理沖突的方法將一組關(guān)鍵字key映像到一個(gè)有限的連續(xù)的地址區(qū)間上,并以關(guān)鍵字在地址區(qū)間中的像作為數(shù)據(jù)元素在表中的存儲(chǔ)位置,這種表便被稱(chēng)為散列表,這一映像過(guò)程稱(chēng)為散列,所得存儲(chǔ)位置稱(chēng)為散列地址。

    關(guān)鍵字、散列函數(shù)以及散列表的關(guān)系如下圖所示:

201581193721542.jpg (401×270)

     1、散列函數(shù)

    散列函數(shù)是從關(guān)鍵字到地址區(qū)間的映像。

    好的散列函數(shù)能夠使得關(guān)鍵字經(jīng)過(guò)散列后得到一個(gè)隨機(jī)的地址,以便使一組關(guān)鍵字的散列地址均勻地分布在整個(gè)地址區(qū)間中,從而減少?zèng)_突。

    常用的構(gòu)造散列函數(shù)的方法有:

    (1)、直接定址法

    取關(guān)鍵字或關(guān)鍵字的某個(gè)線性函數(shù)值為散列地址,即:

    h(key) = key   或 h(key) = a * key + b

    其中a和b為常數(shù)。

    (2)、數(shù)字分析法

    (3)、平方取值法

    取關(guān)鍵字平方后的中間幾位為散列地址。

    (4)、折疊法

    將關(guān)鍵字分割成位數(shù)相同的幾部分(最后一部分的位數(shù)可以不同),然后取這幾部分的疊加和(舍去進(jìn)位)作為散列地址。

    (5)、除留余數(shù)法

    取關(guān)鍵字被某個(gè)不大于散列表表長(zhǎng)m的數(shù)p除后所得的余數(shù)為散列地址,即:

    h(key) = key MOD p    p ≤ m

    (6)、隨機(jī)數(shù)法

    選擇一個(gè)隨機(jī)函數(shù),取關(guān)鍵字的隨機(jī)函數(shù)值為它的散列地址,即:

    h(key) = random(key)

    其中random為隨機(jī)函數(shù)。

    2、處理沖突

    對(duì)不同的關(guān)鍵字可能得到同一散列地址,即key1 ≠ key2,而h(key1)= h(key2),這種現(xiàn)象稱(chēng)為沖突。具有相同函數(shù)值的關(guān)鍵字對(duì)該散列函數(shù)來(lái)說(shuō)稱(chēng)作同義詞。

    在一般情況下,散列函數(shù)是一個(gè)壓縮映像,這就不可避免地會(huì)產(chǎn)生沖突,因此,在創(chuàng)建散列表時(shí)不僅要設(shè)定一個(gè)好的散列函數(shù),而且還要設(shè)定一種處理沖突的方法。

    常用的處理沖突的方法有:

    (1)、開(kāi)放定址法

    hi =(h(key) + di) MOD m     i =1,2,…,k(k ≤ m-1)

    其中,h(key)為散列函數(shù),m為散列表表長(zhǎng),di為增量序列,可有下列三種取法:

    1)、di = 1,2,3,…,m-1,稱(chēng)線性探測(cè)再散列;

    2)、di = 12,-12,22,-22,32,…,±k2 (k ≤m/2),稱(chēng)二次探測(cè)再散列;

    3)、di = 偽隨機(jī)數(shù)序列,稱(chēng)偽隨機(jī)探測(cè)再散列。

    (2)、再散列法

    hi = rhi(key)   i = 1,2,…,k

    rhi均是不同的散列函數(shù)。

    (3)、鏈地址法

    將所有關(guān)鍵字為同義詞的數(shù)據(jù)元素存儲(chǔ)在同一線性鏈表中。假設(shè)某散列函數(shù)產(chǎn)生的散列地址在區(qū)間[0,m-1]上,則設(shè)立一個(gè)指針型向量void *vec[m],其每個(gè)分量的初始狀態(tài)都是空指針。凡散列地址為i的數(shù)據(jù)元素都插入到頭指針為vec[i]的鏈表中。在鏈表中的插入位置可以在表頭或表尾,也可以在表的中間,以保持同義詞在同一線性鏈表中按關(guān)鍵字有序排列。

    (4)、建立一個(gè)公共溢出區(qū)


相關(guān)的C語(yǔ)言解釋

hash.h
哈希表數(shù)據(jù)結(jié)構(gòu)&&接口定義頭文件

 

  #ifndef HASH_H 
  #define HASH_H 
   
  #define HASH_TABLE_INIT_SIZE 7 
   
  #define SUCCESS 1 
  #define FAILED 0 
   
   
  /** 
   * 哈希表槽的數(shù)據(jù)結(jié)構(gòu) 
   */ 
  typedef struct Bucket { 
    char *key; 
    void *value; 
    struct Bucket *next; 
  } Bucket; 
   
  /** 
   * 哈希表數(shù)據(jù)結(jié)構(gòu) 
   */ 
  typedef struct HashTable { 
    int size;  // 哈希表大小 
    int elem_num;  // 哈希表已經(jīng)保存的數(shù)據(jù)元素個(gè)數(shù) 
    Bucket **buckets; 
  } HashTable; 
   
  int hashIndex(HashTable *ht, char *key); 
  int hashInit(HashTable *ht); 
  int hashLookup(HashTable *ht, char *key, void **result); 
  int hashInsert(HashTable *ht, char *key, void *value); 
  int hashRemove(HashTable *ht, char *key); 
  int hashDestory(HashTable *ht); 
  #endif 


hash.c
哈希表操作函數(shù)具體實(shí)現(xiàn)

  #include <stdio.h> 
  #include <stdlib.h> 
  #include <string.h> 
  #include "hash.h" 
   
  /** 
   * 初始化哈希表 
   * 
   * T = O(1) 
   * 
   */ 
  int hashInit(HashTable *ht) 
  { 
    ht->size = HASH_TABLE_INIT_SIZE; 
    ht->elem_num = 0; 
    ht->buckets = (Bucket **)calloc(ht->size, sizeof(Bucket *)); 
   
    if (ht->buckets == NULL) 
      return FAILED; 
    else 
      return SUCCESS; 
  } 
   
  /** 
   * 散列函數(shù) 
   * 
   * T = O(n) 
   * 
   */ 
  int hashIndex(HashTable *ht, char *key) 
  { 
    int hash = 0; 
   
    while (*key != '\0') { 
      hash += (int)*key; 
      key ++; 
    } 
   
    return hash % ht->size; 
  } 
   
  /** 
   * 哈希查找函數(shù) 
   * 
   * T = O(n) 
   * 
   */ 
  int hashLookup(HashTable *ht, char *key, void **result) 
  { 
    int index = hashIndex(ht, key); 
   
    Bucket *bucket = ht->buckets[index]; 
   
    while (bucket) { 
      if (strcmp(bucket->key, key) == 0) { 
        *result = bucket->value; 
        return SUCCESS; 
      } 
      bucket = bucket->next; 
    } 
   
    return FAILED; 
  } 
   
   
  /** 
   * 哈希表插入操作 
   * 
   * T = O(1) 
   * 
   */ 
  int hashInsert(HashTable *ht, char *key, void *value) 
  { 
    int index = hashIndex(ht, key); 
   
    Bucket *org_bucket, *tmp_bucket; 
    org_bucket = tmp_bucket = ht->buckets[index]; 
   
    // 檢查key是否已經(jīng)存在于hash表中 
    while (tmp_bucket) { 
      if (strcmp(tmp_bucket->key, key) == 0) { 
        tmp_bucket->value = value; 
        return SUCCESS; 
      } 
      tmp_bucket = tmp_bucket->next; 
    } 
   
    Bucket *new = (Bucket *)malloc(sizeof(Bucket)); 
     
    if (new == NULL)  return FAILED; 
   
    new->key = key; 
    new->value = value; 
    new->next = NULL; 
   
    ht->elem_num += 1; 
   
    // 頭插法 
    if (org_bucket) { 
      new->next = org_bucket; 
    } 
   
    ht->buckets[index] = new; 
   
    return SUCCESS;  
  } 
   
  /** 
   * 哈希刪除函數(shù) 
   * 
   * T = O(n) 
   * 
   */ 
  int hashRemove(HashTable *ht, char *key) 
  { 
    int index = hashIndex(ht, key); 
   
    Bucket *pre, *cur, *post; 
   
    pre = NULL; 
    cur = ht->buckets[index]; 
   
    while (cur) { 
      if (strcmp(cur->key, key) == 0) { 
        post = cur->next; 
         
        if (pre == NULL) { 
          ht->buckets[index] = post; 
        } else { 
          pre->next = post; 
        } 
   
        free(cur); 
   
        return SUCCESS; 
      } 
   
      pre = cur; 
      cur = cur->next; 
    } 
   
    return FAILED; 
  } 
   
  /** 
   * 哈希表銷(xiāo)毀函數(shù) 
   * 
   * T = O(n) 
   */ 
  int hashDestory(HashTable *ht) 
  { 
    int i; 
    Bucket *cur, *tmp; 
   
    cur = tmp = NULL; 
   
    for (i = 0; i < ht->size; i ++) { 
      cur = ht->buckets[i]; 
   
      while (cur) { 
        tmp = cur->next; 
        free(cur); 
        cur = tmp; 
      } 
    } 
   
    free(ht->buckets); 
   
    return SUCCESS; 
  } 


test.c
單元測(cè)試文件

  #include <stdio.h> 
  #include <stdlib.h> 
  #include <string.h> 
  #include <assert.h> 
  #include "hash.h" 
   
  int main(int argc, char **argv) 
  { 
    HashTable *ht = (HashTable *)malloc(sizeof(HashTable)); 
    int result = hashInit(ht); 
   
    assert(result == SUCCESS); 
   
    /* Data */ 
    int int1 = 10; 
    int int2 = 20; 
    char str1[] = "Hello World!"; 
    char str2[] = "Value"; 
    char str3[] = "Hello New World!"; 
   
    /* to find data container */ 
    int *j = NULL; 
    char *find_str = NULL; 
   
    /* Test Key Insert */ 
    printf("Key Insert:\n"); 
    hashInsert(ht, "FirInt", &int1); 
    hashInsert(ht, "FirStr", str1); 
    hashInsert(ht, "SecStr", str2); 
    printf("Pass Insert\n"); 
   
    /* Test Key Lookup*/ 
    printf("Key Lookup:\n"); 
    result = hashLookup(ht, "FirStr", &find_str); 
    assert(result == SUCCESS); 
    printf("pass lookup, the value is %s\n", find_str); 
   
    /* Test Update */ 
    printf("Key Update:\n"); 
    hashInsert(ht, "FirStr", str3); 
    result = hashLookup(ht, "FirStr", &find_str); 
    assert(result == SUCCESS); 
    printf("pass update, the value is %s\n", find_str); 
   
    return 0; 
  } 


編譯方法

gcc -Wall -g -o main test.c hash.c

運(yùn)行結(jié)果

201581194105153.png (590×141)

開(kāi)放尋址法
在開(kāi)放尋址法(open addressing)中,所有的元素都存放在散列表里。亦即,每個(gè)表項(xiàng)或包含動(dòng)態(tài)集合的一個(gè)元素,或包含NIL。當(dāng)查找一個(gè)元素時(shí),要檢查所有的表項(xiàng),直到找到所需的元素,或者最終發(fā)現(xiàn)該元素不在表中。不像在鏈接法中,這沒(méi)有鏈表,也沒(méi)有元素存放在散列表外。在這種方法中,散列表可能會(huì)被填滿(mǎn),以致于不能插入任何新的元素,但裝載因子a是絕對(duì)不會(huì)超過(guò)1的

線性探測(cè)法
第一次沖突移動(dòng)1個(gè)單位,再次沖突時(shí),移動(dòng)2個(gè),再次沖突,移動(dòng)3個(gè)單位,依此類(lèi)推

它的散列函數(shù)是:H(x) = (Hash(x) + F(i)) mod TableSize, 且F(0) = 0

舉例(騰訊面試題目)

已知一個(gè)線性表(38, 25, 74, 63, 52, 48),假定采用散列函數(shù) h(key) = key % 7 計(jì)算散列地址,并散列存儲(chǔ)在散列表 A[0..6]中,若采用線性探測(cè)方法解決沖突,則在該散列表上進(jìn)行等概率成功查找的平均長(zhǎng)度為 ?

下邊模擬線性探測(cè):

    38 % 7 == 3,  無(wú)沖突, ok
    25 % 7 == 4, 無(wú)沖突, ok
    74 % 7 == 4, 沖突, (4 + 1)% 7 == 5, 無(wú)沖突,ok
    63 % 7 == 0, 無(wú)沖突, ok
    52 % 7 == 3, 沖突, (3 + 1) % 7 == 4. 沖突, (4 + 1) % 7 == 5, 沖突, (5 + 1)%7 == 6,無(wú)沖突,ok
    48 % 7 == 6, 沖突, (6 + 1) % 7 == 0, 沖突,  (0 + 1) % 7 == 1,無(wú)沖突,ok


畫(huà)圖如下:

201581194134572.jpg (466×117)

平均查找長(zhǎng)度 = (1 + 3 + 1 + 1 + 2 + 3) % 6 = 2

線性探測(cè)方法比較容易實(shí)現(xiàn),但它卻存在一個(gè)問(wèn)題,稱(chēng)為一次群集(primary clustering).隨著時(shí)間的推移,連續(xù)被占用的槽不斷增加,平均查找時(shí)間也隨著不斷增加。集群現(xiàn)象很容易出現(xiàn),這是因?yàn)楫?dāng)一個(gè)空槽前有i個(gè)滿(mǎn)的槽時(shí),該空槽為下一個(gè)將被占用的槽的概率是 (i + 1) / n.連續(xù)占用的槽的序列會(huì)變得越來(lái)越長(zhǎng),因而平均查找時(shí)間也會(huì)隨之增加

平方探測(cè)
為了避免上面提到的一個(gè)群集的問(wèn)題:第一次沖突時(shí)移動(dòng)1(1的平方)個(gè)單位,再次沖突時(shí),移動(dòng)4(2的平方)個(gè)單位,還沖突,移動(dòng)9個(gè)單位,依此類(lèi)推。F(i) = i * i

相關(guān)文章

  • C++實(shí)現(xiàn)進(jìn)程間通信(IPC)的終極指南

    C++實(shí)現(xiàn)進(jìn)程間通信(IPC)的終極指南

    進(jìn)程間通信(IPC,Inter-Process Communication),指至少兩個(gè)進(jìn)程或線程間傳送數(shù)據(jù)或信號(hào)的一些技術(shù)或方法,下面小編來(lái)和大家深入介紹一下C++實(shí)現(xiàn)進(jìn)程間通信(IPC)的相關(guān)方法吧
    2025-04-04
  • C語(yǔ)言實(shí)現(xiàn)電話簿項(xiàng)目管理

    C語(yǔ)言實(shí)現(xiàn)電話簿項(xiàng)目管理

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)電話簿項(xiàng)目管理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • OpenCV實(shí)現(xiàn)霍夫變換直線檢測(cè)

    OpenCV實(shí)現(xiàn)霍夫變換直線檢測(cè)

    這篇文章主要為大家詳細(xì)介紹了OpenCV實(shí)現(xiàn)霍夫變換直線檢測(cè),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • 編輯器寫(xiě)C語(yǔ)言輸出中文亂碼問(wèn)題及解決

    編輯器寫(xiě)C語(yǔ)言輸出中文亂碼問(wèn)題及解決

    這篇文章主要介紹了編輯器寫(xiě)C語(yǔ)言輸出中文亂碼問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • C++類(lèi)型轉(zhuǎn)換和IO流操作處理教程

    C++類(lèi)型轉(zhuǎn)換和IO流操作處理教程

    這篇文章主要給大家介紹了關(guān)于C++類(lèi)型轉(zhuǎn)換和IO流操作處理的相關(guān)資料,標(biāo)準(zhǔn)C++為了加強(qiáng)類(lèi)型轉(zhuǎn)換的可視性,引入了四種命名的強(qiáng)制類(lèi)型轉(zhuǎn)換操作符,文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下
    2023-08-08
  • 基于MFC實(shí)現(xiàn)貪吃蛇小游戲

    基于MFC實(shí)現(xiàn)貪吃蛇小游戲

    這篇文章主要為大家詳細(xì)介紹了基于MFC實(shí)現(xiàn)貪吃蛇小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • C語(yǔ)言詳細(xì)講解strcpy strcat strcmp函數(shù)的模擬實(shí)現(xiàn)

    C語(yǔ)言詳細(xì)講解strcpy strcat strcmp函數(shù)的模擬實(shí)現(xiàn)

    這篇文章主要介紹了怎樣用C語(yǔ)言模擬實(shí)現(xiàn)strcpy與strcat和strcmp函數(shù),strcpy()函數(shù)是C語(yǔ)言中的一個(gè)復(fù)制字符串的庫(kù)函數(shù),strcat()函數(shù)的功能是實(shí)現(xiàn)字符串的拼接,strcmp()函數(shù)作用是比較字符串str1和str2是否相同
    2022-05-05
  • C++中順序表操作的示例代碼

    C++中順序表操作的示例代碼

    這篇文章主要為大家詳細(xì)介紹了C++中順序表的基礎(chǔ)操作的相關(guān)代碼,主要有順序表的輸出、插入和刪除數(shù)據(jù)等,感興趣的小伙伴可以了解一下
    2022-10-10
  • C語(yǔ)言實(shí)現(xiàn)五子棋游戲

    C語(yǔ)言實(shí)現(xiàn)五子棋游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • C語(yǔ)言用數(shù)組實(shí)現(xiàn)反彈球消磚塊

    C語(yǔ)言用數(shù)組實(shí)現(xiàn)反彈球消磚塊

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言用數(shù)組實(shí)現(xiàn)反彈球消磚塊,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05

最新評(píng)論

天门市| 彰化县| 调兵山市| 宜川县| 呼伦贝尔市| 南川市| 蓝山县| 叙永县| 鹤峰县| 义乌市| 科技| 泾阳县| 克拉玛依市| 醴陵市| 上饶市| 宝应县| 宁夏| 拉萨市| 四会市| 德惠市| 绥阳县| 七台河市| 焉耆| 正镶白旗| 拉萨市| 婺源县| 彝良县| 龙口市| 高阳县| 大英县| 岱山县| 沾益县| 淮滨县| 洛隆县| 成安县| 夏津县| 宜兰县| 华容县| 富宁县| 平定县| 三门县|