哈希表入门:用 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 + i² )或双重哈希(多个哈希函数) |
支持泛型 | 使用void* 指针结合类型标签,或通过 C11 _Generic 关键字实现 |
3. 注意事项
- 内存管理:使用完哈希表后需调用
free
释放data
和结构体内存,避免内存泄漏 - 负载因子控制:合理设置负载因子(元素数 / 表大小),平衡空间与时间效率
- 哈希函数设计:对于字符串等复杂数据,需设计更均匀的哈希函数(如 DJB2、FNV 算法)
七、总结
本文通过一个简单的 C 语言实例,演示了哈希表的基本实现流程:
- 哈希函数将数据映射到表中位置
- 开放寻址法(线性探测)处理哈希冲突
- 动态内存管理实现表的初始化与数据存储
哈希表的核心优势在于 ** 平均 O (1)** 的插入和查找时间复杂度,但其性能高度依赖哈希函数设计和冲突处理策略。实际开发中需根据数据特性选择合适的哈希表实现方案。
八、扩展思考
- 如何实现哈希表的查找(
get
)功能? - 尝试用链表法(链地址法)改写冲突处理逻辑
- 分析线性探测与二次探测的性能差异
欢迎在评论区分享你的思路与实践!
相关文章:
哈希表入门:用 C 语言实现简单哈希表(开放寻址法解决冲突)
目录 一、引言 二、代码结构与核心概念解析 1. 数据结构定义 2. 初始化函数 initList 3. 哈希函数 hash 4. 插入函数 put(核心逻辑) 开放寻址法详解: 三、主函数验证与运行结果 1. 测试逻辑 2. 运行结果分析 四、完整代码 五、优…...

[华为eNSP] 在eNSP上实现IPv4地址以及IPv4静态路由的配置
设备名称配置 重命名设备以及关闭信息提示 此处以R1演示,R2R3以此类推 <Huawei>system-view [Huawei]sysname R1#关闭提示 undo info-center enable 配置路由接口IP地址 R1 [R1]interface GigabitEthernet 0/0/1[R1-GigabitEthernet0/0/1]ip address 10.0.…...

2024年第十五届蓝桥杯青少组c++国赛真题——快速分解质因数
2024年第十五届蓝桥杯青少组c国赛真题——快速分解质因数 题目可点下方去处,支持在线编程,在线测评~ 快速分解质因数_C_少儿编程题库学习中心-嗨信奥 题库收集了历届各白名单赛事真题和权威机构考级真题,覆盖初赛—省赛—国赛&am…...

【动手学MCP从0到1】2.1 SDK介绍和第一个MCP创建的步骤详解
SDK介绍和第一个MCP 1. 安装SDK2. MCP通信协议3. 基于stdio通信3.1 服务段脚本代码3.2 客户端执行代码3.2.1 客户端的初始化设置3.2.2 创建执行进行的函数3.2.3 代码优化 4. 基于SSE协议通信 1. 安装SDK 开发mcp项目,既可以使用Anthropic官方提供的SDK,…...
基于MyBatis插件实现动态表名解决多环境单一数据库问题
业务场景 在为某新能源汽车厂商进行我司系统私有化部署时,在预演环境和生产环境中,客户仅提供了一个 MySQL 数据库实例。为了确保数据隔离并避免不同环境之间的数据冲突,常规做法是为每个环境创建独立的表(如通过添加环境前缀或后…...

测试面试题总结一
目录 列表、元组、字典的区别 nvicat连接出现问题如何排查 mysql性能调优 python连接mysql数据库方法 参数化 pytest.mark.parametrize 装饰器 list1 [1,7,4,5,5,6] for i in range(len(list1): assert list1[i] < list1[i1] 这段程序有问题嘛? pytest.i…...
Spring Boot应用多环境打包与Shell自动化部署实践
一、多环境配置管理(Profile方案) 推荐方案:通过Maven Profiles实现环境隔离 在pom.xml中定义不同环境配置,避免硬编码在application.yml中: <profiles><!-- 默认环境 --><profile><id>node…...

【深度学习】14. DL在CV中的应用章:目标检测: R-CNN, Fast R-CNN, Faster R-CNN, MASK R-CNN
深度学习在计算机视觉中的应用介绍 深度卷积神经网络(Deep convolutional neural network, DCNN)是将深度学习引入计算机视觉发展的关键概念。通过模仿生物神经系统,深度神经网络可以提供前所未有的能力来解释复杂的数据模式&…...
grpc的二进制序列化与http的文本协议对比
grpc的二进制序列化与http的文本协议对比 1. 二进制格式 vs 文本格式2. 编码机制:Varint 与固定长度3. 没有字段名与标点4. 较少的元信息开销4.1 HTTP/1.1 请求的元信息组成与开销4.1.1 各部分字节数示例 4.2 HTTP/2 帧结构与 HPACK 头部压缩4.2.1 HEADERS 开销对比…...
Linux 环境下 PPP 拨号的嵌入式开发实现
一、PPP 协议基础与嵌入式应用场景 PPP (Point-to-Point Protocol) 是一种在串行线路上传输多协议数据包的通信协议,广泛应用于拨号上网、VPN 和嵌入式系统的远程通信场景。在嵌入式开发中,PPP 常用于 GPRS/3G/4G 模块、工业路由器和物联网设备的网络连接…...

UE 材质基础第三天
飘动的旗帜 错乱的贴图排序,创建一个材质函数 可以用在地面材质 体积云材质制作 通过网盘分享的文件:虚幻引擎材质宝典.rar 链接: https://pan.baidu.com/s/1AYRz2V5zQFaitNPA5_JbJw 提取码: cz1q --来自百度网盘超级会员v6的分享...

【Github/Gitee Webhook触发自动部署-Jenkins】
Github/Gitee Webhook触发自动部署-Jenkins #mermaid-svg-hRyAcESlyk5R2rDn {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hRyAcESlyk5R2rDn .error-icon{fill:#552222;}#mermaid-svg-hRyAcESlyk5R2rDn .error-tex…...
软件工程专业本科毕业论文模板
以下是软件工程专业本科毕业论文的通用模板框架,结合学术规范与工程实践要求,涵盖从需求分析到测试验证的全流程结构,并附格式说明与写作建议: 一、前置部分 1. 封面 - 包含论文标题(简明反映研究核心,如“…...

新松机械臂 2001端口服务的客户端例程
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 源码指引:github源…...

电脑网络重置,找不到原先自家的WIFI,手机还能正常连接并上网
问题排查:1、电脑感觉网络太慢,因此打算点击了网络重置 2、点击提示会删除网络,在五分钟后关机重启 3、从设备管理器设备的无线wifi属性-事件中发现删除记录 4、选择更新驱动程序 5、从列表中选取 6、更改回老驱动版本 备选方案&#…...

期末复习(学习)之机器学习入门基础
上课没听过报道。欢迎补充交流! 前言:老师画的重点其实可以完全不用看,我这里只是看了一眼书顺着书本敲一遍。 比较干货的部分,直接看学习通的内容就好。最重要的是把学习通的内容记好。 目录 老师划的重点:P50 结构…...

网络各类型(BMA,NBMA,P2P)
网络类型—基于二层(数据链路层)使用的协议不同从而导致数据包封装方式不同,工作方式也有所区别,从而对网络本身进行分类 一、网络类型分类 2. 关键差异对比 1. HDLC(高级数据链路控制协议) 协议特点&…...
Linux 库文件的查看和管理
Linux 库文件说明1、库文件的类型2、库文件存储路径3、库文件查找顺序 Linux 库文件管理1、查看动态库相关信息2、添加动态库查找路径 Linux 库文件说明 1、库文件的类型 Linux 中的库文件本质上就是封装好的功能模块,某个应用程序如果要实现某个功能,…...
Java设计模式深度解析:策略模式的核心原理与实战应用
目录 策略模式基础解析策略模式实现指南策略模式典型应用场景Java生态中的策略模式实践策略模式进阶技巧策略模式最佳实践总结与展望1. 策略模式基础解析 1.1 核心概念与定义 策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法族,将每个算法封装成独立…...

【计算机网络】第3章:传输层—概述、多路复用与解复用、UDP
目录 一、概述和传输层服务 二、多路复用与解复用 三、无连接传输:UDP 四、总结 (一)多路复用与解复用 (二)UDP 一、概述和传输层服务 二、多路复用与解复用 三、无连接传输:UDP 四、总结 (…...
6、在树莓派上安装 NTP(Network Time Protocol )服务的步骤
在树莓派上安装 NTP(Network Time Protocol )服务的步骤: 1. 安装 NTP 服务 打开树莓派终端,输入以下命令更新软件包列表: sudo apt-get update然后安装 NTP 服务: sudo apt-get install ntp2. 配置 NT…...

神经符号AI的企业应用:结合符号推理与深度学习的混合智能
💡 技术前沿: 神经符号AI代表了人工智能发展的新阶段,它将深度学习的模式识别能力与符号推理的逻辑分析能力有机结合,创造出更加智能、可解释且可靠的AI系统。这种混合智能技术正在重塑企业的智能化应用,从自动化决策到…...

VSCode 中 C/C++ 安装、配置、使用全攻略:小白入门指南
引言 本文为Windows系统下安装配置与使用VSCode编写C/C代码的完整攻略,示例机器为Windows11。 通过本文的指导,你可以成功在Windows 机器上上使用VSCode进行C/C开发。 在文章开始之前,你可以先阅读下面这段话,以便于对步骤有个大…...

重温经典算法——希尔排序
版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl 基本原理 希尔排序是插入排序的改进版,通过按增量分组并逐步缩小增量实现排序。时间复杂度取决于增量序列,平均约为 O(n log n) 到 O(n^(3/2))&…...

CortexON:开源的多代理AI系统无缝自动化和简化日常任务
简介 CortexON是一个开源的多代理AI系统,灵感来自Manus和OpenAI DeepResearch等高级代理平台。CortexON旨在无缝自动化和简化日常任务,擅长执行复杂的工作流程,包括全面的研究任务、技术操作和复杂的业务流程自动化。 技术架构 CortexON的技…...

海信IP810N-海思MV320芯片-安卓9-2+16G-免拆优盘卡刷固件包
海信IP810N-海思MV320芯片-安卓9-216G-免拆优盘卡刷固件包 线刷方法:(新手参考借鉴一下) 1.准备一个优盘,最佳是4G,卡刷强刷刷机,用一个usb2.0的8G以下U盘,fat32,2048块单分区格式化…...
【Golang】使用gin框架导出excel和csv文件
目录 1、背景2、go库【1】excel库下载【2】csv标准库 3、代码示例4、使用方法 1、背景 项目中可能会遇到导入导出一批数据的功能,对于批量大数据可能用表格的方式直观性更好,所以本篇文件来讲一下go中导出excel和csv文件的方式。 2、go库 【1】excel库…...
【unity游戏开发入门到精通——通用篇】AssetBundle(AB包)和AssetBundleBrowser的使用介绍
文章目录 前言1、什么是AssetBundle?2、AB包与Resources系统对比3、AB包核心价值一、AB包打包工具Asset Bundle Browser1、下载安装AssetBundles-Browser2、打开Asset Bundle Browser窗口3、如何让资源关联AB包二、AssetBundleBrowser参数相关1、Configure 配置页签2、Build 构…...

2025年6月4日收获
Authorization Authorization是一种通用的、标准化的权限控制和认证的通用框架,它能够使跨系统和跨域的身份验证和授权管理更容易,使不同应用程序之间能够更轻松地实现单点登录(SSO)、用户身份验证和授权控制等。 在前端使用 axi…...

leetcode hot100 链表(二)
书接上回: leetcode hot100 链表(一)-CSDN博客 8.删除链表的倒数第N个结点 class Solution { public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* currhead;int len0;while(curr){currcurr->next;len;}int poslen-n…...