數據結構——哈希技術及鏈地址法

目錄

一、哈希的定義

二、哈希沖突定義

?三、構造哈希函數的方法

四、四種解決哈希沖突的方法

4.1 開放地址法

4.2 鏈地址法

4.3 再散列函數法

4.4 公共區溢出法

五、鏈地址法結構體設計

六、基本操作的實現

6.1 哈希函數

6.2 初始化

6.3 插入值

6.4 刪除值

6.5 查找值

6.6 打印

6.7 測試用例?

一、哈希的定義

哈希(Hash)是一種將任意長度的輸入數據通過哈希算法轉換成固定長度(通常是固定長度的字符串)輸出的過程。哈希算法通常會將輸入數據映射為一個固定長度的字符串,這個字符串通常稱為哈希值或摘要。

也就是說,我們只需要通過某個函數f ,使得存儲位置=f(關鍵字),那么我們可以通過查找關鍵字不需要比較就可以獲得需要記錄的存儲位置。

哈希函數具有以下特點:

  1. 輸入數據相同,輸出的哈希值必定相同。
  2. 不同的輸入數據,哈希值是獨立的,即不會有沖突。
  3. 哈希值的長度是固定的,不會隨輸入數據的長度變化而變化。
  4. 哈希值是不可逆的,即無法從哈希值還原出原始的輸入數據。

哈希既是一種存儲方法,也是一種查找方法。

二、哈希沖突定義

兩個或多個關鍵碼key1!=key2,但是通過哈希函數的計算,得出的結果卻相等,這種現象就是發生了哈希沖突。

例如:f(x)=x %10

例如上述的86和66,計算得出都應該存放在6號下標,沖突了。?

?三、構造哈希函數的方法

構造哈希函數的方法有很多種。其中一種常見的方法是利用數學運算來將輸入數據映射到固定大小的哈希值。

以下是一些構造哈希函數的方法:

  1. 直接尋址法:將輸入數據直接作為索引值,獲取對應的哈希值。

  2. 除留余數法:將輸入數據除以一個數,取余數作為哈希值。

  3. 平方取中法:對輸入數據進行平方運算,然后取中間幾位作為哈希值。

  4. 折疊法:將輸入數據分割成固定長度的片段,對每個片段進行加法或異或運算,最終得到哈希值。

  5. 隨機哈希函數:使用隨機數生成器生成哈希函數,將輸入數據與隨機數進行運算來得到哈希值。

  6. 加法哈希函數:將輸入數據中的每個字符轉換成ASCII碼,然后求和得到哈希值。

  7. 乘法哈希函數:將輸入數據乘以一個常數,取乘積的某幾位作為哈希值。

四、四種解決哈希沖突的方法

4.1 開放地址法

? ? ? ? ?線性探測法:

? ? ?但是這種方法會存在一種情況:兩個值本來都不是同義詞卻需要爭奪一個地址,這種現象叫做堆積?

? ? ?二次探測法:

所以我們想著再探測的時候,盡可能的即向左探測,也向右探測,并且探測的幅度還在呈指數爆炸的趨勢增加。這種方法叫做二次探測法

? ? ? 隨機探測法:?

4.2 鏈地址法

當哈希表用鏈地址法處理沖突時,每個槽位都存儲一個鏈表或其他數據結構,該鏈表用于存儲哈希值相同的鍵值對。當需要查找、插入或刪除一個鍵值對時,首先計算對應的哈希值,然后根據哈希值找到對應的槽位,最后在該槽位上的鏈表中進行操作。

優點:鏈地址法的優點是容易實現和理解,可以有效地處理哈希沖突,適用于存儲大量數據的情況。

缺點:鏈地址法可能會浪費一定的空間用于存儲鏈表的指針,同時在遍歷鏈表時可能會引起緩存未命中。

4.3 再散列函數法

當發生哈希沖突時,再散列函數法會根據一個特定的規則選擇另一個哈希函數,將原始的關鍵字重新哈希,生成一個新的哈希值。然后,再檢查新的哈希值對應的槽位是否為空,如果為空則將數據插入該位置,如果不為空則繼續使用再散列函數生成新的哈希值,直到找到一個合適的位置為止。

4.4 公共區溢出法

公共區溢出法與鏈地址法不同的是,公共區溢出法在哈希表的每個槽位中直接存儲鍵值對,當發生哈希沖突時,會在其他空槽位中尋找可用的位置來存儲沖突的鍵值對。

具體來說,當插入一個鍵值對時,如果計算得出的哈希值對應的槽位已經被占用,那么就會根據某種探測序列在哈希表中查找下一個可用的空槽位,并將鍵值對存儲在該位置上。具體的探測序列可以是線性探測、二次探測、雙重散列等。

    優點:

    1. 公共區溢出法不需要額外的數據結構來存儲沖突的鍵值對,節省了額外的空間開銷。
    2. 可以提高數據的局部性,減少緩存未命中的可能性。
    3. 插入、查找和刪除操作時,不需要額外的指針操作,節省了內存。

    缺點:

    1. 當哈希表變滿時,可能會增加插入操作的復雜度,可能導致性能下降。
    2. 如果探測序列選擇不當,可能會導致產生大量的聚集現象,影響查找效率。
    3. 刪除操作可能較為繁瑣,需要標記刪除的鍵值對。

    五、鏈地址法結構體設計

    鏈地址法中有效節點的結構體設計:1.數據域? 2.指針域

    //鏈地址法有效節點的結構體設計
    typedef int ElemType;
    typedef struct List_Node
    {ElemType data;  //數據域struct List_Node* next; //指針域
    }List_Node, *PList_Node; 
    

    鏈地址法中輔助節點的結構體設計: 1. 數組,有INIT_SIZE個格子,每一個格子存放的是單鏈表的輔助節點。

    //鏈地址法輔助節點的結構體設計
    #define INITSIZE 12
    typedef struct List_address
    {struct List_Node arr[INITSIZE];
    }List_address;

    六、基本操作的實現

    6.1 哈希函數

    哈希函數實現,就是計算給定元素的哈希值。其中,ElemType代表元素的類型,INITSIZE代表哈希表的初始大小。

    哈希函數采用了取余運算符%來計算元素的哈希值,具體操作是將給定元素val除以INITSIZE后取余數。取余運算可以將元素的值映射到一個較小的范圍內,使得得到的哈希值在哈希表的合法索引范圍內。

    代碼實現如下:

    //哈希函數
    int Hash(ElemType val)
    {return val % INITSIZE;
    }


    6.2 初始化

    通過for循環,調用單鏈表中的初始化函數即可完成初始化。

    //初始化
    void Init_List_Address(List_address* pla)
    {for (int i = 0; i < INITSIZE; i++){InitList(&pla->arr[i]);}
    }


    6.3 插入值

    1. 首先,函數接受一個指向哈希表的指針pla和待插入的元素值val作為參數。

    2. 通過調用之前提到的哈希函數Hash(val)來計算元素val的哈希值,并將其賦值給index變量。

    3. 接著,通過調用malloc()函數動態分配內存,創建一個新的節點pnewnode,用于存儲待插入的元素。

    4. 如果內存分配成功(即pnewnode不為NULL),則將元素值val賦給新節點的數據域data

    5. 將新節點的next指針指向哈希表中對應索引位置的鏈表頭節點,以實現在鏈表頭插法的方式將新節點插入到哈希表中。

    6. 最后,返回true表示插入成功。

    代碼實現如下:

    //插入值
    bool Insert(List_address* pla, ElemType val)
    {assert(pla != NULL);int index = Hash(val);Node* pnewnode = (Node*)malloc(sizeof(Node));if (NULL == pnewnode)return false;pnewnode->data = val;pnewnode->next = pla->arr[index].next;pla->arr[index].next = pnewnode;return true;
    }
    


    6.4 刪除值

    1. 首先,函數接受一個指向哈希表的指針pla和待刪除的元素值val作為參數。

    2. 通過調用哈希函數Hash(val)計算元素val的哈希值,并將其賦值給index變量。

    3. 調用Hash_List_Address_Search()函數來查找哈希表中是否存在值為val的節點,將返回的節點賦給指針 q。

    4. 如果節點qNULL,即未找到待刪除的元素,則返回false表示刪除失敗。

    5. 如果找到了值為val的節點q,則進入循環,遍歷哈希表中索引為index的鏈表,找到節點q的前驅節點,即節點p

    6. 在找到節點q的前驅節點p后,將前驅節點的next指針指向節點q的后繼節點,實現刪除節點q的操作。

    7. 通過調用free(q)釋放節點q占用的內存空間,并將指針q設為NULL,避免懸空指針。

    8. 最后,返回true表示刪除成功。

    代碼實現如下:

    bool Delete(List_address* pla, ElemType val)
    {assert(pla != NULL);int index = Hash(val);Node* q = Hash_List_Address_Search(pla, val);if (q == NULL)return false;//此時代碼執行到這,證明val值節點存在在index下標里面的單鏈表上Node* p = &pla->arr[index];for (; p->next != q; p = p->next);//此時代碼執行到這里,證明p和q都就位p->next = q->next;free(q);q = NULL;return true;
    }


    6.5 查找值

    1. 首先,函數接受一個指向哈希表的指針pla和待查找的元素值val作為參數。

    2. 通過調用哈希函數Hash(val)計算元素val的哈希值,并將其賦值給index變量。

    3. 查找哈希表中索引為index的單鏈表的起始節點,并將其賦給指針p。

    4. 進入循環,遍歷哈希表中索引為index的鏈表,逐個比較節點中存儲的數據值是否等于待查找的元素值val。

    5. 如果找到與待查找的元素值相等的節點,則返回該節點的指針p,表示找到了目標節點。

    6. 如果在整個鏈表中都沒有找到與待查找的元素值相等的節點,則循環結束后,返回NULL,表示未找到目標節點。

    代碼實現如下:

    struct Node* Hash_List_Address_Search(List_address* pla, ElemType val)
    {assert(pla != NULL);int index = Hash(val);Node* p = pla->arr[index].next;for (; p != NULL; p = p->next){if (p->data == val){return p;}}return NULL;
    }


    6.6 打印

    void Show(List_address* pla)
    {for (int i = 0; i < INITSIZE; i++){printf("第%d行:", i);Node* p = pla->arr[i].next;for (; p != NULL;p = p->next){printf("%d->", p->data);}printf("\n");}
    }

    6.7 測試用例?

    int main()
    {List_address head;Init_List_Address(&head);Insert(&head, 12);Insert(&head, 67);Insert(&head, 56);Insert(&head, 16);Insert(&head, 25);Insert(&head, 37);Insert(&head, 22);Insert(&head, 29);Insert(&head, 15);Insert(&head, 47);Insert(&head, 48);Insert(&head, 34);Show(&head);printf("-----------------------------\n");Delete(&head, 25);Delete(&head, 12345);Show(&head);return 0;
    }

    運行結果如下:

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/bicheng/76591.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76591.shtml
    英文地址,請注明出處:http://en.pswp.cn/bicheng/76591.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    算法思想之前綴和(二)

    歡迎拜訪&#xff1a;霧里看山-CSDN博客 本篇主題&#xff1a;算法思想之前綴和(二) 發布時間&#xff1a;2025.4.11 隸屬專欄&#xff1a;算法 目錄 滑動窗口算法介紹核心思想大致步驟 例題和為 K 的子數組題目鏈接題目描述算法思路代碼實現 和可被 K 整除的子數組題目鏈接題目…

    開源的7B參數OCR視覺大模型:RolmOCR

    1. 背景介紹 早些時候&#xff0c;Allen Institute for AI 發布了 olmOCR&#xff0c;這是一個基于 Qwen2-VL-7B 視覺語言模型&#xff08;VLM&#xff09;的開源工具&#xff0c;用于處理 PDF 和其他復雜文檔的 OCR&#xff08;光學字符識別&#xff09;。開發團隊對該工具的…

    移動端六大語言速記:第14部分 - 數據庫操作

    移動端六大語言速記:第14部分 - 數據庫操作 本文將對比Java、Kotlin、Flutter(Dart)、Python、ArkTS和Swift這六種移動端開發語言在數據庫操作方面的特性,幫助開發者理解和掌握各語言的數據庫編程能力。 14. 數據庫操作 14.1 SQL查詢 各語言SQL查詢實現方式對比: 特性Ja…

    有哪些反爬機制可能會影響Python爬取視頻?如何應對這些機制?

    文章目錄 前言常見反爬機制及影響1. IP 封禁2. 驗證碼3. 請求頭驗證4. 動態加載5. 加密與混淆6. 行為分析 應對方法1. 應對 IP 封禁2. 應對驗證碼3. 應對請求頭驗證4. 應對動態加載5. 應對加密與混淆6. 應對行為分析 前言 在使用 Python 爬取視頻時&#xff0c;會遇到多種反爬…

    ESP32開發入門:基于VSCode+PlatformIO環境搭建指南

    前言 ESP32作為一款功能強大的物聯網開發芯片&#xff0c;結合PlatformIO這一現代化嵌入式開發平臺&#xff0c;可以大幅提升開發效率。本文將詳細介紹如何在VSCode中搭建ESP32開發環境&#xff0c;并分享實用開發技巧。 一、環境安裝&#xff08;Windows/macOS/Linux&#xf…

    DeepSeek:穿透行業知識壁壘的搜索引擎攻防戰

    DeepSeek&#xff1a;穿透行業知識壁壘的搜索引擎攻防戰 文 / 產業智能觀察組&#xff08;人機協同創作&#xff09; 一、搜索引擎的"認知折疊"危機 2024年Q1數據顯示&#xff0c;百度搜索結果前10頁中&#xff0c;61.7%的內容存在"偽專業化"現象——看似…

    SQL 外鍵(Foreign Key)詳細講解

    1. 什么是外鍵&#xff1f;?? ??定義??&#xff1a;外鍵是數據庫表中的一列&#xff08;或一組列&#xff09;&#xff0c;用于??建立兩個表之間的關聯關系??。外鍵的值必須匹配另一個表的主鍵&#xff08;Primary Key&#xff09;或唯一約束&#xff08;Unique Con…

    5G中的DU和CU的作用

    在5G網絡架構中&#xff0c;CU&#xff08;Centralized Unit&#xff0c;集中單元&#xff09; 和 DU&#xff08;Distributed Unit&#xff0c;分布單元&#xff09; 是無線接入網&#xff08;RAN&#xff09;的重要組成部分&#xff0c;它們的分工和作用如下&#xff1a; 1.…

    深度解析 n8n:強大的開源工作流自動化平臺

    在數字化時代&#xff0c;企業和個人面臨著日益復雜的工作流程和多樣化的應用工具&#xff0c;如何高效整合這些資源、實現工作流的自動化成為提升效率的關鍵。n8n 作為一款開源的工作流自動化平臺&#xff0c;憑借其強大的功能、廣泛的應用集成能力和靈活的部署方式&#xff0…

    ruby超高級語法

    以下是 Ruby 中一些 極度硬核 的語法和底層特性&#xff0c;涉及元編程的深淵、虛擬機原理、語法黑魔法等&#xff0c;適用于追求極限的 Ruby 開發者&#xff1a; 高級語法一 一、語法核彈級操作 1. 動態修改繼承鏈 class A; def foo; "A"; end end class B; def …

    flutter 獲取通話記錄和通訊錄

    Dart SDK version is 3.7.01 dependencies:flutter:sdk: flutterpermission_handler: ^11.0.1 # 權限管理flutter_contacts: ^1.1.92call_log: ^5.0.5cupertino_icons: ^1.0.8dev_dependencies:flutter_test:sdk: flutterflutter_lints: ^5.0.0 2 contact_and_calls_page.da…

    bash腳本手動清空mysql表數據

    文章目錄 1、bash腳本手動清空mysql表數據 1、bash腳本手動清空mysql表數據 #!/bin/bash# 配置區域&#xff08;修改此處&#xff09; MYSQL_USER"root" MYSQL_PASSWORD"123456" MYSQL_HOST"localhost" DATABASES("hps-base:base_test_ite…

    Spark Core編程

    一文讀懂Spark Core編程核心要點 最近在學習大數據處理框架Spark&#xff0c;今天來給大家分享一下Spark Core編程中非常重要的內容&#xff0c;包括RDD算子、累加器和廣播變量&#xff0c;希望能幫助大家更好地理解和掌握Spark編程。先來說說RDD算子&#xff0c;它是Spark編程…

    SDP(一)

    SDP(Session Description Protocol)會話描述協議相關參數 Session Description Protocol Version (v): 0 --說明&#xff1a;SDP當前版本號 Owner/Creator, Session Id (o): - 20045 20045 IN IP4 192.168.0.0 --說明&#xff1a;發起者/創建者 會話ID&#xff0c;那么該I…

    HarmonyOS:組件布局保存至相冊

    一&#xff0c;需求背景 有這樣一個需求&#xff0c;將頁面上的某個自定義組件以圖片的形式保存至相冊。 二&#xff0c;需求拆解 根據需求分析&#xff0c;可將需求拆解成兩步&#xff1a; 1&#xff0c;將組件轉換成圖片資源&#xff1b; 2&#xff0c;將圖片保存到相冊…

    算法中的數論基礎

    算法中的數論基礎 本篇文章適用于算法考試或比賽之前的臨場復習記憶&#xff0c;沒有復雜公式推理&#xff0c;基本上是知識點以及函數模版&#xff0c;涵蓋取模操作、位運算的小技巧、組合數、概率期望、進制轉換、最大公約數、最小公倍數、唯一分解定理、素數、快速冪等知識…

    Redis下載穩定版本5.0.4

    https://www.redis.net.cn/download/ Redis下載 Redis 版本號采用標準慣例:主版本號.副版本號.補丁級別,一個副版本號就標記為一個標準發行版本,例如 1.2,2.0,2.2,2.4,2.6,2.8,奇數的副版本號用來表示非標準版本,例如2.9.x發行版本是Redis 3.0標準版本的非標準發行版本…

    ?UniApp 安卓打包完整步驟(小白向)

    ? ?一、環境準備? ?安裝 HBuilderX? 下載最新版 HBuilderX 并安裝&#xff08;官方 IDE&#xff0c;支持一鍵打包&#xff09;?16確保已安裝 Node.js&#xff08;用于依賴管理&#xff09;?26 ?配置 Android 開發環境? 安裝 ?Java JDK 17?&#xff08;建議選擇穩定…

    【Springboot知識】Springboot配置加載機制深入解讀

    文章目錄 配置加載概述**Spring Boot 配置加載機制詳解****一、配置加載順序&#xff08;優先級由低到高&#xff09;****二、關鍵配置機制說明****1. Profile 機制****2. 外部化配置****3. 配置屬性綁定到 Bean****4. 動態覆蓋配置** **三、配置加載流程圖****2. 配置導入&…

    AI圖像生成

    要通過代碼實現AI圖像生成&#xff0c;可以使用深度學習框架如TensorFlow、PyTorch或GANs等技術。下面是一個簡單的示例代碼&#xff0c;演示如何使用GANs生成手寫數字圖像&#xff1a; import torch import torchvision import torchvision.transforms as transforms import …