目录

一、引言

二、代码结构与核心概念解析

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 语言实例,演示了哈希表的基本实现流程:

  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…