当前位置: 首页 > news >正文

哈希表(C语言版)

文章目录

  • 哈希表
    • 原理
    • 实现(无自动扩容功能)
      • 代码
      • 运行结果
    • 分析
    • 应用

哈希表

如何统计一段文本中,小写字母出现的次数?

显然,我们可以用数组 int table[26] 来存储每个小写字母出现的次数,而且这样处理,效率奇高。假如我们想知道字母’k’出现的次数,直接访问元素 table['k' - 'a'] 即可,时间复杂度为O(1)。

在现实生活中,我们经常需要存储键值对(key-value)数据,比如上面的 ‘a’:10, ‘b’:6,再比如账号:个人信息,关键字:网页等等。如果键的取值范围很小(比如上面的例子),那么我们可以用数组存储,为每一个键绑定一个索引。

但是,如果键的取值范围很大,那么数组的方式就行不通了。哈希表就是被设计用来解决这样一个问题的~

原理

哈希表的核心设计分为两个部分:

  1. 哈希函数。哈希函数将 key 转换为数组中的一个索引。理想情况下不同的 key 都能转换成不同的索引值。当然这只是理想情况,所以我们还需要处理两个或者多个 key 都散列到相同索引值的情况 (哈希冲突)。

    优秀的哈希函数需要满足这些特性(拓展):
    a. 运算速度快。
    b. 尽量使键平均分布
    c. 逆向非常困难
    d. 对数据非常敏感
    e. 哈希冲突的概率非常小哈希函数:模拟等概率随机分布事件。
    
  2. 处理哈希冲突。

    • 开放地址法:线性探测法、平方探测法、再散列法
    • 拉链法

实现(无自动扩容功能)

这里,我们也采用常用的拉链法来解决哈希冲突,如下图所示:

在这里插入图片描述

代码

// Hash.h#include <stdint.h>
#define N 10typedef char* K;
typedef char* V;typedef struct node {K key;V val;struct node* next;
} Node;typedef struct {Node* table[N];int size;int capacity;uint32_t hashseed; // 哈希种子 保证哈希桶位置映射的随机性
} HashMap;HashMap* hashmap_create();
void hashmap_destroy(HashMap* map);V hashmap_put(HashMap* map, K key, V val);
V hashmap_get(HashMap* map, K key);
void hashmap_delete(HashMap* map, K key);
// Hash.c#include "hash.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>HashMap* hashmap_create() {// calloc 方法HashMap* hashmap = (HashMap*)calloc(1, sizeof(HashMap));if (hashmap) {hashmap->size = 0;hashmap->capacity = N;hashmap->hashseed = time(NULL);}return hashmap;
}// hashfunc()
/* murmurhash2 */
uint32_t hash(const void* key, int len, uint32_t seed) {const uint32_t m = 0x5bd1e995;const int r = 24;uint32_t h = seed ^ len;const unsigned char* data = (const unsigned char*)key;while (len >= 4) {uint32_t k = *(uint32_t*)data;k *= m;k ^= k >> r;k *= m;h *= m;h ^= k;data += 4;len -= 4;}switch (len){case 3: h ^= data[2] << 16;case 2: h ^= data[1] << 8;case 1: h ^= data[0];h *= m;};h ^= h >> 13;h *= m;h ^= h >> 15;return h;
}V hashmap_put(HashMap* map, K key, V val) {// a. 如果key不存在,添加key-val,并返回NULL// b. 如果key存在,更新key关联的val,返回原来的valint idx = hash(key, strlen(key), map->hashseed) % map->capacity; // 确定哈希桶Node* cur = map->table[idx];while (cur) {if (strcmp(cur->key, key) == 0) { // 如果key存在V oldVal = cur->val;cur->val = val;printf("有重复key, 已将旧值:%s 更换为新值:%s\n", oldVal, val);return oldVal;}cur = cur->next;} // cur == NULL// key不存在的情况,插入新的键值对Node* newNode = (Node*)malloc(sizeof(Node));newNode->key = key;newNode->val = val;newNode->next = map->table[idx]; // 头插法map->table[idx] = newNode; // 更新哈希桶的地址map->size++;printf("插入键值对 key: %s  val: %s\n", key, val);return NULL;
}V hashmap_get(HashMap* map, K key) {// a. 如果key不存在,返回NULL// b. 如果key存在,返回key关联的valint idx = hash(key, strlen(key), map->hashseed) % map->capacity; // 确定哈希桶Node* cur = map->table[idx];while (cur) {if (strcmp(cur->key, key) == 0) { // key 存在printf("找到了目标键:%s 对应的值为:%s\n", cur->key, cur->val);return cur->val;}cur = cur->next;}// key不存在printf("没找到目标键 %s 对应的键值对\n", key);return NULL;
}void hashmap_delete(HashMap* map, K key) {int idx = hash(key, strlen(key), map->hashseed) % map->capacity; // 确定哈希桶Node* cur = map->table[idx];Node* prev = NULL;while (cur) {if (strcmp(cur->key, key) == 0) {  // 找到了目标键if (prev == NULL)  // 第一个结点map->table[idx] = cur->next;else prev->next = cur->next;printf("键值对 key: %s val: %s 已释放\n", cur->key, cur->val);free(cur);map->size--;return;}prev = cur;cur = cur->next;}// 没有找到目标键printf("没找到目标键 %s 对应的键值对,无法删除\n", key);
}void hashmap_destroy(HashMap* map) {// 1. 释放所有结点printf("即将释放哈希表中共 %d 对键值对\n", map->size);for (int i = 0; i < map->capacity; i++) {Node* cur = map->table[i];while (cur) {Node* freeNode = cur;cur = cur->next;printf("键值对 key: %s val: %s 已释放\n", freeNode->key, freeNode->val);free(freeNode);} // cur == NULL}// 2. 释放map->tablefree(map->table);// 3. 释放map结构体free(map);printf("哈希表释放成功\n");
}
// main.c
#include "hash.h"
#include <stdlib.h>
#include <stdio.h>int main(void) {HashMap* map = hashmap_create();hashmap_put(map, "1", "tom");hashmap_put(map, "2", "jack");hashmap_get(map, "1");hashmap_put(map, "1", "jane");hashmap_get(map, "1");hashmap_get(map, "100");hashmap_delete(map, "1");hashmap_get(map, "1");hashmap_put(map, "3", "musk");hashmap_put(map, "4", "musk");hashmap_put(map, "5", "musk");hashmap_put(map, "6", "musk");hashmap_destroy(map);return 0;
}

运行结果

在这里插入图片描述

分析

在哈希函数保证 key 平均分布的前提下,那么哈希表的性能就取决于链表的平均长度 (L)。

put : O(L)

​ 先对 key 进行哈希,找到对应的链表,然后遍历链表,判断是添加结点还是更新结点。

get : O(L)

​ 先对 key 进行哈希,找到对应的链表,然后遍历链表,找到对应的结点。

delete : O(L)

​ 先对 key 进行哈希,找到对应的链表,然后遍历链表,删除对应的结点。

如果我们想在常数时间复杂度内, 完成哈希表的增删查操作,那么我们就得控制链表的平均长度不超过某个值。这个值我们称之为加载因子(load factor),也就是链表平均长度可以达到的最大值。

因此,当元素个数达到一定的数目的时候,我们就需要对数组进行扩容(哈希种子也需要重新生成,防止极端情况:所有结点都在一个哈希桶中),然后把所有元素重新映射到哈希表中。

应用

哈希表的应用很广,比如 C++ 中的 unordered_map , unordered_set 和 Java 中的 HashMap, HashSet 底层的数据结构都是哈希表。再比如,常用的缓存中间件 Redis,也大量使用了哈希表数据结构。

相关文章:

哈希表(C语言版)

文章目录 哈希表原理实现(无自动扩容功能)代码运行结果 分析应用 哈希表 如何统计一段文本中&#xff0c;小写字母出现的次数? 显然&#xff0c;我们可以用数组 int table[26] 来存储每个小写字母出现的次数&#xff0c;而且这样处理&#xff0c;效率奇高。假如我们想知道字…...

内容中台驱动企业数字化内容管理高效协同架构

内容概要 在数字化转型加速的背景下&#xff0c;企业对内容管理的需求从单一存储向全链路协同演进。内容中台作为核心支撑架构&#xff0c;通过统一的内容资源池与智能化管理工具&#xff0c;重塑了内容生产、存储、分发及迭代的流程。其核心价值在于打破部门壁垒&#xff0c;…...

LLaMA-Factory DeepSeek-R1 模型 微调基础教程

LLaMA-Factory 模型 微调基础教程 LLaMA-FactoryLLaMA-Factory 下载 AnacondaAnaconda 环境创建软硬件依赖 详情LLaMA-Factory 依赖安装CUDA 安装量化 BitsAndBytes 安装可视化微调启动 数据集准备所需工具下载使用教程所需数据合并数据集预处理 DeepSeek-R1 可视化微调数据集处…...

vue 文件下载(导出)excel的方法

目前有一个到处功能的需求&#xff0c;这是我用过DeepSeek生成的导出&#xff08;下载&#xff09;excel的一个方法。 1.excel的文件名是后端生成的&#xff0c;放在了响应头那里。 2.这里也可以自己制定文件名。 3.axios用的是原生的axios&#xff0c;不要用处理过的&#xff…...

【第4章:循环神经网络(RNN)与长短时记忆网络(LSTM)— 4.3 RNN与LSTM在自然语言处理中的应用案例】

咱今天来聊聊在人工智能领域里,特别重要的两个神经网络:循环神经网络(RNN)和长短时记忆网络(LSTM),主要讲讲它们在自然语言处理里的应用。你想想,平常咱们用手机和别人聊天、看新闻、听语音助手说话,背后说不定就有 RNN 和 LSTM 在帮忙呢! 二、RNN 是什么? (一)…...

LLMs Ollama

LLMs 即大型语言模型&#xff08;Large Language Models&#xff09;&#xff0c;是人工智能领域基于深度学习的重要技术&#xff0c;以下是关于它的详细介绍&#xff1a; 定义与原理 定义&#xff1a;LLMs 是一类基于深度学习的人工智能模型&#xff0c;通过海量数据和大量计…...

Blackbox.AI:高效智能的生产力工具新选择

前言 在当今数字化时代&#xff0c;一款高效、智能且功能全面的工具对于开发者、设计师以及全栈工程师来说至关重要。Blackbox.AI凭借其独特的产品特点&#xff0c;在众多生产力工具中脱颖而出&#xff0c;成为了我近期测评的焦点。以下是我对Blackbox.AI的详细测评&#xff0…...

计算机专业知识【 轻松理解数据库四大运算:笛卡尔积、选择、投影与连接】

在数据库的世界里&#xff0c;有几个关键的运算操作&#xff0c;就像是神奇的魔法工具&#xff0c;能帮助我们对数据进行各种处理和组合。今天&#xff0c;咱们就来聊聊笛卡尔积运算、选择运算、投影运算和连接运算这四大运算&#xff0c;用超简单的例子让小白也能轻松理解。 …...

C/C++字符串格式化全解析:从printf到std::format的安全演进与实战指南

目录 C 语言中的格式化函数对比 1. printf / fprintf / sprintf 的异同 C 中的字符串格式化 1. 流式输出 (std::ostringstream) 2. C20/23 格式化库 (std::format&#xff0c;需编译器支持) 跨语言对比与最佳实践 实战建议 总结 C 语言中的格式化函数对比 1. printf / …...

【C++】stack 和 queue 的适配器模式与实现

> &#x1f343; 本系列为初阶C的内容&#xff0c;如果感兴趣&#xff0c;欢迎订阅&#x1f6a9; > &#x1f38a;个人主页:[小编的个人主页])小编的个人主页 > &#x1f380; &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐文章 > ✌️ &#x1f91e; &#x1…...

【python】You-Get

文章目录 1、介绍2、安装与使用文档3、下载图片4、下载视频5、下载音乐6、参考 1、介绍 You-Get is a tiny command-line utility to download media contents (videos, audios, images) from the Web, in case there is no other handy way to do it. 源码&#xff1a;https…...

PHP基础部分

但凡是和输入、写入相关的一定要预防别人植入恶意代码! HTML部分 语句格式 <br> <hr> 分割符 <p>插入一行 按住shift 输入! 然后按回车可快速输入html代码(VsCode需要先安装live server插件) html:<h1>标题 数字越大越往后</h1> <p…...

gitee SSH 公钥设置教程

Gitee 提供了基于 SSH 协议的 Git 服务,在使用 SSH 协议访问仓库仓库之前,需要先配置好账户 SSH 公钥。 1、生成秘钥 Windows 用户建议使用 Windows PowerShell 或者 Git Bash,在 命令提示符 下无 cat 和 ls 命令。 ssh-keygen -t ed25519 -C "Gitee SSH Key"中间…...

Java零基础入门笔记:(3)程序控制

前言 本笔记是学习狂神的java教程&#xff0c;建议配合视频&#xff0c;学习体验更佳。 【狂神说Java】Java零基础学习视频通俗易懂_哔哩哔哩_bilibili Scanner对象 之前我们学的基本语法中我们并没有实现程序和人的交互&#xff0c;但是Java给我们提供了这样一个工具类&…...

鸡兔同笼问题

鸡兔同笼问题是这样一个问题&#xff1a; 现有鸡、兔合装在一个笼子里。数头一共100个头&#xff0c;数脚一共300只脚。问有多少只鸡多少只兔&#xff1f; 在这里讨论这个问题的解法当然太小儿科了。但是y_tab这个C语言解释器只提供了1维数组。如果需要用到2维数组时&#xff…...

【Pytorch 库】自定义数据集相关的类

torch.utils.data.Dataset 类torch.utils.data.DataLoader 类自定义数据集示例1. 自定义 Dataset 类2. 在其他 .py 文件中引用和使用该自定义 Dataset torch_geometric.data.Dataset 类torch_geometric.data.Dataset VS torch.utils.data.Dataset 详细信息&#xff0c;参阅 tor…...

electron打包基本教程

从0开始搭建 概要步骤基础软件运行项目打包项目 注意事项 概要 将html打包成桌面的主流有electron和nwjs&#xff0c;nwjs更加简单&#xff0c;但是使用效果不如electron&#xff0c;electron打包比较麻烦&#xff0c;但是效果比较好&#xff0c;反正各有优势和缺点 步骤 基…...

实现pytorch注意力机制-one demo

主要组成部分&#xff1a; 1. 定义注意力层&#xff1a; 定义一个Attention_Layer类&#xff0c;接受两个参数&#xff1a;hidden_dim&#xff08;隐藏层维度&#xff09;和is_bi_rnn&#xff08;是否是双向RNN&#xff09;。 2. 定义前向传播&#xff1a; 定义了注意力层的…...

深入Flask:如何优雅地处理HTTP请求与响应

哈喽,大家好,我是木头左! 本文将带你深入了解如何在Flask中优雅地处理HTTP请求和响应,让你的应用更加高效、安全和用户友好。 创建一个简单的Flask应用 让从创建一个最简单的Flask应用开始: from flask import Flaskapp = Flask(__name__)@app.route(/) def...

JVM ②-双亲委派模型 || 垃圾回收GC

这里是Themberfue 在上节课对内存区域划分以及类加载的过程有了简单的了解后&#xff0c;我们再了解其他两个较为重要的机制&#xff0c;这些都是面试中常考的知识点&#xff0c;有必要的话建议背出来&#xff0c;当然不是死记硬背&#xff0c;而是要有理解的背~~~如果对 JVM …...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…...