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

哈希表(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;效率奇高。假如我们想知道字…...

3.5 使用Tokenizer编解码文本:从原理到企业级实践

使用Tokenizer编解码文本:从原理到企业级实践 一、Tokenizer核心原理:文本到数字的魔法转换 1.1 分词算法三大流派 # 不同分词算法对比 tokenization_methods = {"WordPiece": "BERT/ELECTRA", "BPE": "GPT/RoBERTa",...

多表关联查询的优化

文章目录 前言1. 数据库设计优化&#xff1a;深入实践**1.1 规范化与反规范化的决策树****1.2 索引设计的实战技巧** **2. SQL 优化&#xff1a;进阶技巧****2.1 JOIN 顺序与执行计划****2.2 分页查询的深度优化** **3. MyBatis Plus 高级用法****3.1 动态 SQL 规避 N1 查询***…...

亚马逊企业购大客户业务拓展经理张越:跨境电商已然成为全球零售电商领域中熠熠生辉的强劲增长点

2024年12月26日-27日&#xff0c;由中国产业海外发展协会上合-海湾双链专委会指导、极新主办的「重度垂直2024极新AIGC峰会」先后在深圳、香港两地顺利开幕。本届峰会以AI的垂直应用与出海为核心主题&#xff0c;旨在深入探讨AI技术在全球范围内的融合应用与发展趋势&#xff0…...

VirtualBox 中使用 桥接网卡 并设置 MAC 地址

在 VirtualBox 中使用 桥接网卡 并设置 MAC 地址&#xff0c;可以按照以下步骤操作&#xff1a; 步骤 1&#xff1a;设置桥接网卡 打开 VirtualBox&#xff0c;选择你的虚拟机&#xff0c;点击 “设置” (Settings)。进入 “网络” (Network) 选项卡。在 “适配器 1” (Adapt…...

idea无法联网,离线安装插件

插件地址&#xff1a;https://plugins.jetbrains.com/ JetBrains Marketplace 如果无法进入&#xff0c;可以试试 配置hosts 3.163.125.103 plugins.jetbrains.com ip 变了&#xff0c;可以查询个最新的&#xff1a; https://tool.chinaz.com/speedtest/plugins.jetbrai…...

网络安全中的机器学习

当涉及到网络安全时&#xff0c;技术一直是保护系统免受攻击和数据泄露的关键。在这篇论文中&#xff0c;我将介绍一些当前在网络安全领域使用的关键技术&#xff0c;包括加密&#xff0c;身份验证和防火墙。 首先&#xff0c;加密是网络安全中最常见的技术之一。加密是指使用算…...

halcon 条形码、二维码识别、opencv识别

一、条形码 函数介绍 create_bar_code_model * 1.创建条码读取器的模板 * 参数一&#xff1a;通用参数的名称&#xff0c;针对条形码模型进行调整。默认值为空 * 参数二&#xff1a;针对条形码模型进行调整 * 参数三&#xff1a;条形码模型的句柄。 create_bar_code_model (…...

springcloud的组件及作用

Spring Cloud是一个用于构建分布式系统的工具集&#xff0c;它提供了一系列组件来简化微服务架构的开发和部署。以下是一些关键的Spring Cloud组件及其作用&#xff1a; 1. 服务注册与发现 Eureka&#xff1a;Eureka是Spring Cloud中的核心组件之一&#xff0c;用于实现服务注…...

Mybatis <if>、<when>标签判断是否等于单个字符串写法

Mybatis 、标签判断是否等于单个字符串写法 问题描述 当在mybatis sql语句中&#xff0c;需要在标签中判断是否等于单个字符串&#xff0c;例如&#xff1a; <choose><when test"timeUnits d">AND T.NO_POWER_SD > TRUNC(SYSDATE) - TO_NUMBER(#{…...

平板作为电脑拓展屏

有线串流&#xff08;速度更快&#xff09; spacedesk 打开usb对安卓的连接 用usb线直接连接电脑和平板 无线串流&#xff08;延迟高&#xff0c;不推荐&#xff09; todesk pc和手机端同时下载软件&#xff0c;连接后可以进行远程控制或扩展屏幕 spacedesk 连接到同一个…...

Mac访问局域网Jenkins

主要修改两个文件 第一个是 &#xff5e;/Library/LaunchAgents/homebrew.mxcl.jenkins.plist 第二个 ⚠️注意如果是使用 brew 安装的 Jenkins 可以在终端执行brew info jenkins查看你安装 Jenkins 的路径&#xff0c;执行完看控制台 比如我的是 ~ % brew info jenkins >…...

【算法与数据结构】字典树(Trie)详解

目录 一&#xff0c;字典树的定义 二&#xff0c;字典树的代码实现 完整代码详细注释&#xff1a; 测试用例测试结果&#xff1a; 三&#xff0c;处理其他字符 四&#xff0c;内存优化与扩展 1. 内存优化 2. 扩展功能 五&#xff0c;扩展功能支持通配符匹配 六&…...

【JavaEE进阶】MyBatis通过注解实现增删改查

目录 &#x1f343;前言 &#x1f340;打印日志 &#x1f334;传递参数 &#x1f38b;增(Insert) &#x1f6a9;返回主键 &#x1f384;删(Delete) &#x1f332;改(Update) &#x1f333;查(Select) &#x1f6a9;起别名 &#x1f6a9;结果映射 &#x1f6a9;开启驼…...

Deepseek 怼CHATGPT实况

提示词&#xff1a; 用犀利的语言批评ChatGPT 深度思考&#xff08;R1) 嗯&#xff0c;用户让我用犀利的语言批评ChatGPT。首先&#xff0c;我需要理解他们的动机。可能他们遇到了ChatGPT的某些问题&#xff0c;比如回答不准确或者缺乏深度&#xff0c;所以想表达不满。也有…...

【RK3588嵌入式图形编程】-SDL2-构建模块化UI

构建模块化UI 文章目录 构建模块化UI1、概述2、创建UI管理器3、嵌套组件4、继承5、多态子组件6、总结在本文中,将介绍如何使用C++和SDL创建一个灵活且可扩展的UI系统,重点关注组件层次结构和多态性。 1、概述 在前面的文章中,我们介绍了应用程序循环和事件循环,这为我们的…...

Simulink Ststeflow教程 — 2 创建和编辑状态

目录 2.1 创建和编辑状态 2.1.1 状态的创建 2.1.2 创建连接节点 2.1.3 转移 2.1.5 默认转移 在Stateflow模型中&#xff0c;将包含有状态的Stateflow框图称状态图而将不包含任何状态的Stateflow框图称为流程图。其中&#xff0c;状态图是Stateflow最常用的一种形式&#x…...

Fiddler笔记

文章目录 一、与F12对比二、核心作用三、原理四、配置1.Rules:2.配置证书抓取https包3.设置过滤器4、抓取App包 五、模拟弱网测试六、调试1.线上调试2.断点调试 七、理论1.四要素2.如何定位前后端bug 注 一、与F12对比 相同点&#xff1a; 都可以对http和https请求进行抓包分析…...

线上就医全流程医药机构接入文档接口代码-医保就医接口php-demo版本

2025年2月18日11:28:03 国密算法开发库推荐 lpilp/guomi 我测试过php 7.2 - 8.0都可以兼容&#xff0c;如果有能力可以自己开发 目前已经开发了核心的接口的测试demo,并且封装了工具类直接写业务逻辑即可&#xff0c;并且已经有线上项目在使用&#xff0c;如果需要demo代码可…...

Linux升级Anacodna并配置jupyterLab

在使用 Anaconda 的过程中&#xff0c;随着项目和需求的发展&#xff0c;可能需要升级 Anaconda 的 Base 环境中的 Python 版本。本文将详细介绍如何安全地进行升级&#xff0c;包括步骤、代码示例与最终流程图。 升级 Python 一、环境准备 在进行任何升级之前&#xff0c;建…...

com.typesafe.config

com.typesafe.config 是 Typesafe Config 库的核心包&#xff0c;主要用于 统一、灵活地管理应用程序配置&#xff0c;支持从多种格式&#xff08;如 HOCON、JSON、Java Properties&#xff09;加载配置&#xff0c;并提供类型安全的访问接口。以下是其核心功能的详细解析&…...

Recall(召回率)和 Precision(精确率) 的区别和F1分数

Recall与Precision 的区别 一 、概念 Recall&#xff08;召回率&#xff09;和 Precision&#xff08;精确率&#xff09;是信息检索、数据挖掘以及机器学习等领域中评估模型或算法性能的两个重要指标&#xff0c;特别是在分类问题中。 Precision&#xff08;精确率&#xff…...

智能选路+NAT实验 作业

拓扑图 配置ip 防火墙安全区域划分 用户配置 dns透明...

Vue 项目登录的基本流程

Vue 用户登录的基本流程包括以下6个步骤&#xff1a; 步骤&#xff1a; 1. 创建登录表单 在前端&#xff0c;首先要创建一个登录表单&#xff0c;用户输入账号&#xff08;用户名、邮箱、手机号等&#xff09;和密码。 示例&#xff1a;Login.vue <template><div…...

LLM:RAG

原文链接&#xff1a;LLM&#xff1a;RAG 1、RAG 概览 RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09;是一种结合了信息检索&#xff08;IR&#xff09;和 LLM 的技术。它的核心思想是在 LLM 生成回答之前&#xff0c;通过检索相关文档…...

基于Qt 和微信小程序的用户管理系统:WebSocket + SQLite 实现注册与登录

目录 一. 概要 二. 技术栈 三. 系统功能设计 3.1 功能模块 3.2 数据表设计 四. 具体实现 4.1 Qt 服务端 4.1.1 初始化 WebSocket 服务器 4.1.2 用户管理界面 4.2 微信小程序端 4.2.1 注册功能 4.2.2 登录功能 五. 运行效果 六. 源码下载 一. 概要 在物联网和智能设备…...

43.日常算法

1.LCR 142. 训练计划 IV 题目来源 给定两个以 有序链表 形式记录的训练计划 l1、l2&#xff0c;分别记录了两套核心肌群训练项目编号&#xff0c;请合并这两个训练计划&#xff0c;按训练项目编号 升序 记录于链表并返回。 注意&#xff1a;新链表是通过拼接给定的两个链表的…...

策略+适配器模式详解

文章目录 1.策略模式1.目录结构2.Strategy.java 策略接口3.StrategyA.java 策略A4.StrategyB.java 策略B5.StrategyC.java 策略C6.Context.java 策略上下文7.Client.java 客户端8.小结 2.适配器模式1.目录结构2.CustomPaymentProcessor.java 自己的支付接口3.PayPalPaymentServ…...

2024年职高单招或高考计算机类投档线

问题&#xff1a; 这些学校2024年职高单招或高考计算机类投档线分别是多少 回答&#xff1a; 部分学校2024年职高单招或高考计算机类投档线如下&#xff1a; 湖南工业职业技术学院 职高单招&#xff1a;未查询到2024年职高单招计算机类专业明确的录取分数线信息。但在2024年…...

Salesforce 检索Layout的设定

做了许多Object&#xff0c;却想不起来怎么设置我的Listview的项目了。 問題&#xff1a; salesforce 最近参照したオブジェクト 表示項目を変更したいですが、「検索レイアウト」の選択メニューが該当オブジェクトのオブジェクトマネージャーから出てないです。 解決方法&am…...