哈希表入門:用 C 語言實現簡單哈希表(開放尋址法解決沖突)

目錄

一、引言

二、代碼結構與核心概念解析

1. 數據結構定義

2. 初始化函數?initList

3. 哈希函數?hash

4. 插入函數?put(核心邏輯)

開放尋址法詳解:

三、主函數驗證與運行結果

1. 測試邏輯

2. 運行結果分析

四、完整代碼

五、優化方向與注意事項

1. 現有代碼局限性

2. 優化建議

3. 注意事項

六、總結

七、擴展思考


一、引言

哈希表(Hash Table)是一種高效的數據結構,通過哈希函數實現數據的快速查找與插入。本文將通過一段 C 語言代碼實例,帶大家理解哈希表的基本原理,并分析開放尋址法解決哈希沖突的實現邏輯。

二、哈希表基礎概念

1. 定義與核心思想

哈希表(Hash Table,也稱為散列表)是一種根據鍵(Key)直接訪問內存存儲位置的數據結構。它通過哈希函數(Hash Function)將鍵映射到存儲桶(Bucket)或槽位(Slot),從而實現高效的數據插入、查找和刪除操作。

核心思想:將數據的鍵通過哈希函數轉換為數組索引,使數據可以直接定位,無需遍歷整個數據集。

2. 基本結構

一個簡單的哈希表通常由以下部分組成:

  • 數組:作為存儲數據的物理空間,每個位置稱為一個 “槽位” 或 “桶”
  • 哈希函數:將鍵轉換為數組索引的映射函數
  • 沖突處理機制:解決不同鍵映射到相同索引的問題

3. 關鍵操作時間復雜度

  • 插入:平均 O (1),最壞 O (n)(沖突嚴重時)
  • 查找:平均 O (1),最壞 O (n)
  • 刪除:平均 O (1),最壞 O (n)

這種高效性使得哈希表廣泛應用于數據庫索引、緩存系統(如 Redis)、編程語言內置數據結構(如 Python 的字典、Java 的 HashMap)等場景。

三、代碼結構

1. 數據結構定義

typedef struct HashList
{int num;        // 記錄元素個數char *data;     // 哈希表數組
} HashList;
  • num:用于統計哈希表中實際存儲的元素數量
  • data:字符型數組,作為哈希表的存儲載體,大小由宏定義NUM(值為 10)決定

2. 初始化函數?initList

HashList *initList()
{HashList *list = (HashList *)malloc(sizeof(HashList));list->num = 0;list->data = (char *)malloc(sizeof(char) * NUM);for (int i = 0; i < NUM; i++){list->data[i] = 0;  // 初始化為0(空槽)}return list;
}
  • 功能:分配哈希表結構體內存,初始化數組為空槽(0表示空)
  • 關鍵點:動態內存分配確保哈希表可獨立管理內存,初始化空槽為后續沖突處理奠定基礎

3. 哈希函數?hash

int hash(char data)
{return data % NUM;  // 取模運算生成哈希地址
}
  • 設計思路:利用字符 ASCII 碼對哈希表大小NUM取模,將字符映射到[0, 9]的索引范圍內
  • 特點:簡單直觀,但可能產生哈希沖突(不同字符映射到相同索引)

4. 插入函數?put(核心邏輯)

void put(HashList *list, char data)
{if (list->num >= NUM){printf("哈希表已滿,無法插入");return;}int index = hash(data);          // 初始哈希地址int count = 1;                   // 沖突次數計數器// 開放尋址法解決沖突(線性探測)while (list->data[index] != 0)  {index = hash(hash(data) + count);  // 新地址 = 原哈希值 + 增量再取模count++;if (count == NUM)  // 嘗試所有位置后仍無空位{printf("無法找到空位插入");return;}}// 插入數據list->data[index] = data;list->num++;
}
開放尋址法詳解:
  • 沖突處理策略:線性探測(Linear Probing)
    • 當發現哈希地址index被占用時,按index+1, index+2,...的順序依次查找下一個空槽
    • 代碼中通過hash(hash(data) + count)實現等價于(index + count) % NUM的循環探測
  • 終止條件
    • 找到空槽(data[index] == 0
    • 遍歷所有槽位仍無空位(count == NUM

四、主函數驗證與運行結果

1. 測試邏輯

int main()
{HashList *list = initList();put(list, 'A');  // 'A'的ASCII碼為65,65%10=5 → 初始地址5put(list, 'F');  // 'F'的ASCII碼為70,70%10=0 → 初始地址0(假設空槽)// 打印非空槽位for (int i = 0; i < NUM; i++){if (list->data[i] != 0){printf("data[%d]=%c\n", i, list->data[i]);}}printf("哈希表中有%d個元素", list->num);return 0;
}

2. 運行結果分析

假設'A''F'插入時均未發生沖突:

data[5]=A
data[0]=F
哈希表中有2個元素
  • 若插入順序導致沖突(如先插入'K'(ASCII 75,75%10=5)再插入'A'),則'A'會探測到5+1=6號槽位(假設為空)并插入

五、完整代碼

#include <stdio.h>
#include <stdlib.h>
#define NUM 10typedef struct HashList
{int num;char *data;
} HashList;HashList *initList()
{HashList *list = (HashList *)malloc(sizeof(HashList));list->num = 0;list->data = (char *)malloc(sizeof(char) * NUM);for (int i = 0; i < NUM; i++){list->data[i] = 0;}return list;
}int hash(char data)
{return data % NUM;
}void put(HashList *list, char data)
{if (list->num >= NUM){printf("哈希表已滿,無法插入");}int index = hash(data);int count = 1;while (list->data[index] != 0){index = hash(hash(data) + count);count++;}if (count == NUM){printf("無法找到空位插入");}else{list->data[index] = data;list->num++;}
}int main()
{HashList *list = initList();put(list, 'A');put(list, 'F');for (int i = 0; i < NUM; i++){if (list->data[i] != 0){printf("data[%d]=%c\n", i, list->data[i]);}}printf("哈希表中有%d個元素", list->num);return 0;
}

六、優化方向與注意事項

1. 現有代碼局限性

  • 固定大小:哈希表大小由NUM硬編碼,無法動態擴容
  • 單一類型:僅支持字符型數據存儲,可通過泛型改造支持多類型
  • 線性探測缺陷:可能產生 “聚類”(Cluster)現象,導致后續插入效率下降

2. 優化建議

優化點方案
動態擴容當元素個數超過負載因子(如 0.75)時,重新分配更大數組并重新哈希所有元素
改進沖突處理改用二次探測(index + i2)或雙重哈希(多個哈希函數)
支持泛型使用void*指針結合類型標簽,或通過 C11 _Generic 關鍵字實現

3. 注意事項

  • 內存管理:使用完哈希表后需調用free釋放data和結構體內存,避免內存泄漏
  • 負載因子控制:合理設置負載因子(元素數 / 表大小),平衡空間與時間效率
  • 哈希函數設計:對于字符串等復雜數據,需設計更均勻的哈希函數(如 DJB2、FNV 算法)

七、總結

本文通過一個簡單的 C 語言實例,演示了哈希表的基本實現流程:

  1. 哈希函數將數據映射到表中位置
  2. 開放尋址法(線性探測)處理哈希沖突
  3. 動態內存管理實現表的初始化與數據存儲

哈希表的核心優勢在于 ** 平均 O (1)** 的插入和查找時間復雜度,但其性能高度依賴哈希函數設計和沖突處理策略。實際開發中需根據數據特性選擇合適的哈希表實現方案。

八、擴展思考

  1. 如何實現哈希表的查找(get)功能?
  2. 嘗試用鏈表法(鏈地址法)改寫沖突處理邏輯
  3. 分析線性探測與二次探測的性能差異

歡迎在評論區分享你的思路與實踐!

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

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

相關文章

Windows下運行Redis并設置為開機自啟的服務

下載Redis-Windows 點擊redis-windows-7.4.0下載鏈接下載Redis 解壓之后得到如下文件 右鍵install_redis.cmd文件&#xff0c;選擇在記事本中編輯。 將這里改為redis.windows.conf后保存&#xff0c;退出記事本&#xff0c;右鍵后選擇以管理員身份運行。 在任務管理器中能夠…

2025年ESWA SCI1區TOP,改進成吉思汗鯊魚算法MGKSO+肝癌疾病預測,深度解析+性能實測

目錄 1.摘要2.成吉思汗鯊魚優化算法GKSO原理3.MGKSO4.結果展示5.參考文獻6.代碼獲取7.算法輔導應用定制讀者交流 1.摘要 本文針對肝癌&#xff08;HCC&#xff09;早期診斷難題&#xff0c;提出了一種基于改進成吉思汗鯊魚優化算法&#xff08;MGKSO&#xff09;的計算機輔助診…

李沐-動手學深度學習:RNN

1.RNN從零開始實現 import math import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2l#8.3.4節 #batch_size&#xff1a;每個小批量中子序列樣本的數目&#xff0c;num_steps&#xff1a;每個子序列中預定義的時間步數 #loa…

【C++ Qt】多元素控件(ListWidget、TableWidget、TreeWidget)

每日激勵&#xff1a;“不設限和自我肯定的心態&#xff1a;I can do all things。 — Stephen Curry” 緒論?&#xff1a; 本章將通過代碼示例詳細介紹了Qt中QListWidget、QTableWidget和QTreeWidget三種多元素控件的使用方法與核心功能&#xff0c;涵蓋列表的增刪操作、表格…

基于TI DSP控制的光伏逆變器最大功率跟蹤mppt

基于TI DSP&#xff08;如TMS320F28335&#xff09;控制的光伏逆變器最大功率跟蹤&#xff08;MPPT&#xff09;程序通常涉及以下幾個關鍵部分&#xff1a;硬件電路設計、MPPT算法實現、以及DSP的編程。以下是基于TI DSP的光伏逆變器MPPT程序的一個示例&#xff0c;主要采用擾動…

Python實現P-PSO優化算法優化卷積神經網絡CNN回歸模型項目實戰

說明&#xff1a;這是一個機器學習實戰項目&#xff08;附帶數據代碼文檔&#xff09;&#xff0c;如需數據代碼文檔可以直接到文章最后關注獲取。 1.項目背景 隨著人工智能和深度學習技術的快速發展&#xff0c;卷積神經網絡&#xff08;CNN&#xff09;在圖像分類、目標檢測…

計算機視覺入門:OpenCV與YOLO目標檢測

計算機視覺入門&#xff1a;OpenCV與YOLO目標檢測 系統化學習人工智能網站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目錄 計算機視覺入門&#xff1a;OpenCV與YOLO目標檢測摘要引言技術原理對比1. OpenCV&#xff1a;傳統圖像處理與機器學…

【PCB工藝】繪制原理圖 + PCB設計大綱:最小核心板STM32F103ZET6

繪制原理圖和PCB布線之間的聯系,在繪制原理圖的時候,考慮到后續的PCB設計+嵌入式軟件代碼的業務邏輯,需要在繪制原理圖之初涉及到 硬件設計流程的前期規劃。在嵌入式系統開發中,原理圖設計是整個項目的基礎,直接影響到后續的: PCB 布線效率和質量 ☆☆☆重點嵌入式軟件的…

Centos系統搭建主備DNS服務

目錄 一、主DNS服務器配置 1.安裝 BIND 軟件包 2.配置主配置文件 3.創建正向區域文件 4.創建區域數據文件 5.檢查配置語法并重啟服務 二、從DNS服務配置 1.安裝 BIND 軟件包 2.配置主配置文件 3.創建緩存目錄 4.啟動并設置開機自啟 一、主DNS服務器配置 1.安裝 BIN…

LeetCode[513]找樹左下角的值

思路&#xff1a; 找樹左下角的值&#xff0c;有可能這個值不是左葉子節點&#xff0c;可能是右葉子節點&#xff0c;但怎么說這個值都是葉子節點&#xff0c;首先這道題用層序遍歷的思路比如什么隊列和BSF的遞歸都可以做&#xff0c;但我比較喜歡用純遞歸來搞&#xff0c;因為…

ubuntu20.04.5--arm64版上使用node集成java

ubuntu20.04.5arm上使用node集成java #ssh&#xff0c;可選 sudo apt update sudo apt install openssh-server sudo systemctl status ssh sudo systemctl enable ssh sudo systemctl enable --now ssh #防火墻相關&#xff0c;可選 sudo ufw allow ssh sudo ufw allow 22…

更新 Docker 容器中的某一個文件

&#x1f504; 如何更新 Docker 容器中的某一個文件 以下是幾種在 Docker 中更新單個文件的常用方法&#xff0c;適用于不同場景。 ? 方法一&#xff1a;使用 docker cp 拷貝文件到容器中&#xff08;最簡單&#xff09; &#x1f9f0; 命令格式&#xff1a; docker cp <…

JavaEE->多線程:定時器

定時器 約定一個時間&#xff0c;時間到了&#xff0c;執行某個代碼邏輯&#xff08;進行網絡通信時常見&#xff09; 客戶端給服務器發送請求 之后就需要等待 服務器的響應&#xff0c;客戶端不可能無限的等&#xff0c;需要一個最大的期限。這里“等待的最大時間”可以用定時…

html基礎01:前端基礎知識學習

html基礎01&#xff1a;前端基礎知識學習 1.個人建立打造 -- 之前知識的小總結1.1個人簡歷展示1.2簡歷信息填寫頁面 1.個人建立打造 – 之前知識的小總結 1.1個人簡歷展示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8&qu…

uniapp 鍵盤頂起頁面問題

關于uniapp中鍵盤頂起頁面的問題。這是一個在移動應用開發中常見的問題&#xff0c;特別是當輸入框位于頁面底部時&#xff0c;鍵盤彈出會頂起整個頁面&#xff0c;導致頁面布局錯亂。 pages.json 文件內&#xff0c;在需要處理軟鍵盤的頁面添加 softinputMode 配置&#xff1…

使用 React Native 開發鴻蒙運動健康類應用的??高頻易錯點總結??

&#x1f6a8; ??一、環境配置與工程初始化?? ??1. Node.js 版本沖突?? ??現象??&#xff1a;DevEco Studio 報錯 Unsupported Node version&#xff08;鴻蒙 RN 依賴 Node ≥18&#xff09;。??解決??&#xff1a; nvm install 18.16.0 # 強制鎖定版本 ech…

機器學習——聚類算法

一、聚類的概念 根據樣本之間的相似性&#xff0c;將樣本劃分到不同的類別中的一種無監督學習算法。 細節&#xff1a;根據樣本之間的相似性&#xff0c;將樣本劃分到不同的類別中&#xff1b;不同的相似度計算方法&#xff0c;會得到不同的聚類結果&#xff0c;常用的相似度…

Python訓練第四十四天

DAY 44 預訓練模型 知識點回顧&#xff1a; 預訓練的概念常見的分類預訓練模型圖像預訓練模型的發展史預訓練的策略預訓練代碼實戰&#xff1a;resnet18 作業&#xff1a; 嘗試在cifar10對比如下其他的預訓練模型&#xff0c;觀察差異&#xff0c;盡可能和他人選擇的不同嘗試通…

Spring Boot中保存前端上傳的圖片

在Spring Boot中保存前端上傳的圖片可以通過以下步驟實現&#xff1a; 1. 添加依賴 確保在pom.xml中已包含Spring Web依賴&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifact…

應用層協議:HTTP

目錄 HTTP&#xff1a;超文本傳輸協議 1.1 HTTP報文 1.1.1 請求報文 1.1.2 響應報文 1.2 HTTP請求過程和原理 1.2.1 請求過程 1、域名&#xff08;DNS&#xff09;解析 2、建立TCP連接&#xff08;三次握手&#xff09; 3、發送HTTP請求 4、服務器處理請求 5、返回H…