哈希表開散列法(拉鏈法)

来源:https://www.cnblogs.com/zhonglongbo/archive/2018/03/01/8490768.html
-Advertisement-
Play Games

開散列法又叫鏈地址法(開鏈法)。 開散列法:首先對關鍵碼集合用散列函數計算散列地址,具有相同地址的關鍵碼歸於同一子集合,每一個子集合稱為一個桶,各個桶中的元素通過一個單鏈錶鏈接起來,各鏈表的頭結點存儲在哈希表中。 設元素的關鍵碼為37, 25, 14, 36, 49, 68, 57, 11, 散列表 ...


開散列法又叫鏈地址法(開鏈法)。
開散列法:首先對關鍵碼集合用散列函數計算散列地址,具有相同地址的關鍵碼歸於同一子集合,每一個子集合稱為一個桶,各個桶中的元素通過一個單鏈錶鏈接起來,各鏈表的頭結點存儲在哈希表中。

設元素的關鍵碼為37, 25, 14, 36, 49, 68, 57, 11, 散列表為HT[12],表的大小為12,散列函數為Hash(x) = x % 11
Hash(37)=4
Hash(25)=3
Hash(14)=3
Hash(36)=3
Hash(49)=5
Hash(68)=2
Hash(57)=2
Hash(11)=0
使用哈希函數計算出每個元素所在的桶號,同一個桶的鏈表中存放哈希衝突的元素。
開散列
通常,每個桶對應的鏈表結點都很少,將n個關鍵碼通過某一個散列函數,存放到散列表中的m個桶中,那麼每一個桶中鏈表的平均長度為。以搜索平均長度為的鏈表代替了搜索長度為 n 的順序表,搜索效率快的多。
應用鏈地址法處理溢出,需要增設鏈接指針,似乎增加了存儲開銷。事實上:
由於開地址法必須保持大量的空閑空間以確保搜索效率,如二次探查法要求裝載因數a <= 0.7,而表項所占空間又比指針大的多,所以使用鏈地址法反而比開地址法節省存儲空間。

哈希拉鏈相關代碼如下:

使用素數表對齊做哈希表的容量,降低哈希衝突。

size_t HashTableKPrime(size_t N) //獲取素數
{
  int i = 0;
  const int _PrimeSize = 28;
  static const unsigned long _PrimeList [] =
  {
      53ul, 97ul, 193ul, 389ul, 769ul,
      1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
      49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
      1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
      50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
      1610612741ul, 3221225473ul, 4294967291ul
  };
  for (i=0; i<_PrimeSize; i++)
  {
      if (_PrimeList[i] > N)
          return _PrimeList[i];
  }

  return _PrimeList[_PrimeSize-1];
}

開闢拉鏈的鏈式節點

HashNode* BuyHashKNode(KeyType key,ValueType value) //開闢新節點
{
  HashNode *tmp = (HashNode *)malloc(sizeof(HashNode));
  assert(tmp);
  tmp->_key = key;
  tmp->_value = value;
  tmp->_next = NULL;
  return tmp;
}

哈希函數

KeyType HashKFunc(KeyType key,size_t n)
{
    return key%n;
}

哈希拉鏈表的初始化

void HashTableKInit(HashTable *ht,size_t N)//初始化
{
  assert(ht);
  ht->_N = N;
  ht->_size = 0;
  **這裡要特別註意開闢空間是給指針數組**
  ht->_tables = (HashNode **)malloc(sizeof(HashNode*)*ht->_N);
  assert(ht->_tables);
  memset(ht->_tables,0,sizeof(HashNode*)*ht->_N);
}

插入時擴容這塊很關鍵,要重新開闢一塊數組空間,把原先的表中數據映射過來,但是拉鏈節點不用重新開闢,直接把原先的節點拿過來。

int HashTableKInsert(HashTable* ht, KeyType key, ValueType value) //插入
{
    KeyType index;
    HashNode *node = BuyHashKNode(key,value);
    assert(ht);

    if (ht->_N == ht->_size) //擴容
    {
     size_t i = 0;
     HashTable newht;
     HashTableKInit(&newht,HashTableKPrime(ht->_N));
     for (i=0; i<ht->_N; i++)
     {
         if (ht->_tables[i])
         {
             KeyType index;
             HashNode *cur = ht->_tables[i];
             while (cur)
             {
                 HashNode *next = cur->_next;
                 index = HashKFunc(cur->_key,newht._N);

                 cur->_next = newht._tables[index];
                 newht._tables[index] = cur;

                 cur = next;
             }
         }
     }
     free(ht->_tables);
     ht->_tables = newht._tables;
     ht->_N = newht._N;
    }

    index = HashKFunc(key,ht->_N);
    if (ht->_tables[index])
    {
        HashNode *cur = ht->_tables[index];
        while (cur)
        {
            if (cur->_key == key)
                return -1;
            cur = cur->_next;
        }
    }

    node->_next = ht->_tables[index];
    ht->_tables[index] = node;
    ht->_size++;
    return 0;
}

查找函數

HashNode* HashTableKFind(HashTable* ht, KeyType key) //查找
{
    ValueType index = HashKFunc(key,ht->_N);
    assert(ht);

    if (ht->_tables[index])
    {
        if (ht->_tables[index]->_key == key)
            return ht->_tables[index];
        else
        {
            HashNode *cur = ht->_tables[index];
            while (cur)
            {
                if (cur->_key == key)
                    return cur;
                cur = cur->_next;
            }
            return NULL;
        }
    }
    else
        return NULL;
    
}

刪除函數


int HashTableKRemove(HashTable* ht, KeyType key) //刪除
{
    KeyType index = HashKFunc(key,ht->_N);
    assert(ht);
    
    if (ht->_tables[index])
    {
        HashNode *prev = ht->_tables[index];
        HashNode *cur = ht->_tables[index];
        while (cur)
        {
            if (cur == prev && cur->_key == key)
            {
                ht->_tables[index] = cur->_next;
                free(cur);
                cur = NULL;
                ht->_size--;
                return 0;
            }
            else if(cur->_key == key)
            {
                prev-> = cur->_next;
                free(cur);
                cur = NULL;
                ht->_size--;
                return 0;
            }

            prev = cur;
            cur = cur->_next;
        }
        return -1;
    }
    else
        return -1;
}
void HashTableKDestory(HashTable* ht) //銷毀
{
    size_t i = 0;
    assert(ht);
    for (i=0; i<ht->_N;++i)
    {
        if (ht->_tables[i])
        {
            HashNode *cur = ht->_tables[i];
            while (cur)
            {
                HashNode *tmp = cur;
                cur = cur->_next;
                free(tmp);
                tmp = NULL;
            }
        }
    }
    free(ht->_tables);
    ht->_tables = NULL;
    ht->_size = 0;
    ht->_N = 0;
}

測試函數

void TestHashTableK()
{
    HashTable ht;
    HashTableKInit(&ht,3);

    HashTableKInsert(&ht,10,0);
    HashTableKInsert(&ht,11,0);
    HashTableKInsert(&ht,12,0);
    HashTableKInsert(&ht,106,0);
    HashTableKInsert(&ht,53,0);
    HashTableKInsert(&ht,1,0);
    HashTableKInsert(&ht,15,0);
    HashTableKInsert(&ht,0,0);
    HashTableKInsert(&ht,53,0);
    HashTableKInsert(&ht,52,0);
    HashTableKInsert(&ht,104,0);
    HashTableKInsert(&ht,2,0);
    HashTableKInsert(&ht,54,0);
    HashTableKInsert(&ht,108,0);
    HashTableKPrint(&ht);
    printf("\n");


    printf("%d ",HashTableKFind(&ht,2)->_key);
    printf("%d ",HashTableKFind(&ht,53)->_key);
    printf("%d ",HashTableKFind(&ht,0)->_key);
    printf("%d ",HashTableKFind(&ht,12)->_key);
    printf("%p ",HashTableKFind(&ht,156));
    printf("\n\n");


    HashTableKRemove(&ht,2);
    HashTableKRemove(&ht,53);
    HashTableKRemove(&ht,1);
    HashTableKRemove(&ht,54);
    HashTableKRemove(&ht,89);
    HashTableKPrint(&ht);

    HashTableKDestory(&ht);
    HashTableKPrint(&ht);


}

測試結果:
法系拉鏈

相關哈希表概念請看哈希表詳解:這裡寫鏈接內容


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 技術交流的時候遇到了這樣的一個問題,被問及開發中用到的是不是Restful API,我說的是,我們現在用到的不屬於完全是Restful API。因為我瞭解到的Restful API,是 通過具體的URI定位符,找到對應的資源,然後以固定的格式返回數據,這樣的才是Restful API。然而在我模糊的 ...
  • 一、什麼是實體 由標識來區分的對象稱為實體。 實體的定義隱藏了幾個信息: 兩個實體對象,只要它們的標識屬性值相等,哪怕標識屬性以外的所有屬性值都不相等,這兩個對象也認為是同一個實體,這意味著兩個對象是同一實體在其生命周期內的不同階段。 為了能正確區分實體,標識必須唯一。 實體的標識屬性值是不可變的, ...
  • 本文是我原創,首發於美團點評技術團隊博客。原文地址是:https://mp.weixin.qq.com/s/fx6XfBpuzozsJCvllMcCqw。歡迎大家轉載,轉載請註明出處,謝謝~~。 背景 2017年8月25日,我懷著“再也不要在下班時間收到報警”的美好期待,加入美團金融智能支付負責核心 ...
  • 相關內容: 什麼是beautifulsoup bs4的使用 導入模塊 選擇使用解析器 使用標簽名查找 使用find\find_all查找 使用select查找 首發時間:2018-03-02 00:10 什麼是beautifulsoup: 是一個可以從HTML或XML文件中提取數據的Python庫.... ...
  • ######################TypeError: module.__init__() takes at most 2 arguments (3 given)繼承錯誤,沒有繼承正確的類出現問題代碼: 修正後的代碼: ##### 後期遇到我難找到錯誤的問題會繼續更新 ...
  • 今天用Eclipse Java EE版寫了幾個java工程項目,然後再寫java EE項目的jsp頁面時,Tomcat出現了這個異常信息: 解決辦法: 在菜單欄Window——>Preferences——>Server——>Runtime Environments,將列表中已經配置的Tomcat給R ...
  • 一、字元串 在Python中,加了引號的字元都被認為是字元串! 單引號、雙引號、多引號的區別? 單引號和 雙引號沒有任何區別,但是某種情況下需要單雙配合 如 msg = " My name is Small Nine ,I ' m 22 years old !’" 多引號的作用? 多引號的作用就是多 ...
  • 首先繼承Thread類,然後重寫Thread類的run()方法。 Thread類的子類的對象調用start()方法,然後虛擬機就會調用該線程的run()方法。 當程式執行到start()方法時,線程啟動,此時有兩條執行路徑,一條是主方法執行main方法,另一條是線程路徑執行線程run()里的代碼,兩 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...