实现一个简单的哈希表

news2024/12/25 11:49:43

1. 结构体定义 

typedef struct pair {
    int key;                   // 哈希表中的键,通常是整数类型
    char element[20];          // 关联的字符串元素,最多存储20个字符
} DATA, * LPDATA;

 pair 结构体表示哈希表中的一个元素,包含两个字段:

  • key:这是一个整数,作为哈希表的键,通常用于查找或存储值。
  • element:一个字符数组,用来存储与 key 关联的字符串数据。这个字段的大小为 20,这意味着每个元素最多可以存储 19 个字符(最后一个字符用于存储字符串的结束符 '\0')。
typedef struct hashTable {
    LPDATA* table;             // 一个指针数组,存储哈希表的桶,每个桶包含一个指向 DATA 的指针
    int divisor;               // 哈希表的大小,决定了桶的数量
    int curSize;               // 当前哈希表中存储的元素数量
} HASH, * LPHASH;

hashTable 结构体代表哈希表本身,包含以下字段:

  • table:指向一个 LPDATA 类型的指针数组。每个 LPDATA 是一个指向 DATA 结构体的指针,表示哈希表中的一个桶。
  • divisor:哈希表的大小,决定了哈希表中桶的数量。
  • curSize:当前哈希表中已经存储的元素数量。

2. 创建哈希表函数 createHashTable 

LPHASH createHashTable(int p) {
    LPHASH hash = (LPHASH)malloc(sizeof(HASH));  // 分配内存为哈希表结构体
    assert(hash != NULL);  // 确保内存分配成功

    hash->curSize = 0;                // 初始化哈希表的当前大小为 0
    hash->divisor = p;                // 设置哈希表的大小
    hash->table = (LPDATA*)malloc(sizeof(LPDATA) * hash->divisor);  // 为桶数组分配内存
    assert(hash->table != NULL);  // 确保内存分配成功

    // 初始化哈希表中的所有桶指针为 NULL,表示所有桶都是空的
    for (int i = 0; i < hash->divisor; i++) {
        hash->table[i] = NULL;
    }

    return hash;  // 返回创建的哈希表指针
}

 功能createHashTable 函数用于创建一个哈希表:

  • 使用 malloc 为哈希表结构体分配内存。
  • 设置哈希表的大小为 p,并为桶数组 table 分配内存,每个桶存储一个指向 DATA 结构体的指针。
  • 初始化哈希表中的所有桶为 NULL,表示它们还没有存储任何元素。
  • 返回创建的哈希表指针。

3. 查找函数 search 

int search(LPHASH hash, int key) {
    int pos = key % hash->divisor;  // 计算哈希值,得到该元素应放置的初始位置
    int curpos = pos;               // 当前的位置初始化为计算得到的初始位置

    // 线性探测法查找位置,直到找到空位或已有相同 key 的位置
    do {
        if (hash->table[curpos] == NULL || hash->table[curpos]->key == key) {
            return curpos;  // 如果当前位置为空,或者找到了相同的 key,返回当前的位置
        }

        // 如果当前位置被占用且 key 不相同,采用线性探测法,检查下一个位置
        curpos = (curpos + 1) % hash->divisor;  // 如果超出了表的末尾,则环绕到表的开始位置
    } while (curpos != pos);  // 如果回到初始位置,表示表已满

    return -1;  // 如果哈希表已满,返回 -1
}

 功能search 函数使用线性探测法查找合适的位置插入一个元素或查找一个已有的元素。

  • 首先,计算该元素的哈希值,决定该元素应放置的初始位置。
  • 然后,使用线性探测法(如果当前位置被占用,检查下一个位置)查找该位置。如果当前位置为空或者找到了相同 key 的元素,返回当前位置。
  • 如果遍历完整个哈希表(回到起始位置),说明哈希表已满,返回 -1

4. 插入数据函数 insertData 

void insertData(LPHASH hash, DATA data) {
    // 如果哈希表已满,无法插入新数据
    if (hash->curSize >= hash->divisor) {
        printf("Hash table is full!\n");
        return;
    }

    // 查找数据的插入位置
    int pos = search(hash, data.key);

    // 如果返回 -1,表示表已满,无法插入
    if (pos == -1) {
        printf("Hash table is full, cannot insert new data.\n");
        return;
    }

    // 如果该位置为空,插入数据
    if (hash->table[pos] == NULL) {
        hash->table[pos] = (LPDATA)malloc(sizeof(DATA));  // 为该位置分配内存
        assert(hash->table[pos] != NULL);  // 确保内存分配成功

        // 将数据复制到该位置
        memcpy(hash->table[pos], &data, sizeof(DATA));
        hash->curSize++;  // 更新当前哈希表大小
    }
    // 如果该位置已经存在相同的 key,则更新该位置的 element
    else if (hash->table[pos]->key == data.key) {
        // 使用 strncpy 避免字符串溢出,确保 element 的长度不会超过 19 个字符
        strncpy(hash->table[pos]->element, data.element, sizeof(hash->table[pos]->element) - 1);
        hash->table[pos]->element[sizeof(hash->table[pos]->element) - 1] = '\0';  // 确保字符串以 '\0' 结尾
    }
    else {
        printf("Key already exists, but handled by rehashing mechanism.\n");
    }
}

 功能insertData 函数用于将一个 DATA 数据插入哈希表中:

  • 首先检查哈希表是否已满,如果已满,打印提示并返回。
  • 使用 search 函数查找插入位置。如果返回 -1,表示哈希表已满,无法插入。
  • 如果该位置为空,分配内存存储数据并将数据复制到该位置,并增加当前哈希表大小。
  • 如果该位置已存在相同 key,则使用 strncpy 安全地更新该位置的 element 字段。

5. 打印哈希表函数 printHash 

void printHash(LPHASH hash) {
    for (int i = 0; i < hash->divisor; i++) {
        if (hash->table[i] == NULL) {
            printf("NULL\n");  // 如果桶为空,打印 NULL
        }
        else {
            // 打印当前桶中的 key 和 element
            printf("Key:%d Element:%s\n", hash->table[i]->key, hash->table[i]->element);
        }
    }
}

 功能printHash 函数遍历哈希表中的所有桶,打印每个桶的内容:

  • 如果桶为空(即该位置为 NULL),打印 "NULL"。
  • 如果桶不为空,则打印该桶的 keyelement

6. 主函数 main 

int main() {
    // 创建一个大小为 10 的哈希表
    LPHASH hash = createHashTable(10);

    // 定义一个 DATA 数组,包含 4 个元素
    DATA array[4] = { {1, "雷电"}, {11, "春妙"}, {13, "晓月"}, {17, "雪月"} };

    // 将数组中的数据插入到哈希表中
    for (int i = 0; i < 4; i++) {
        insertData(hash, array[i]);
    }

    // 打印哈希表的内容
    printHash(hash);

    // 释放哈希表中每个桶存储的数据内存
    for (int i = 0; i < hash->divisor; i++) {
        if (hash->table[i] != NULL) {
            free(hash->table[i]);  // 释放每个桶中的 DATA 元素内存
        }
    }

    // 释放存储桶指针数组的内存
    free(hash->table);

    // 释放哈希表结构体本身的内存
    free(hash);

    return 0;  // 程序执行成功,返回 0
}

 

  • 创建一个大小为 10 的哈希表。
  • 定义并插入 4 个 DATA 数据元素到哈希表中。
  • 打印哈希表的内容,查看每个桶的 keyelement
  • 在程序结束时,释放所有分配的内存,包括哈希表元素和哈希表结构本身。

完整代码


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <memory.h>

// 定义一个结构体 'pair',表示哈希表中的一个数据元素
// 包含两个字段:key 和 element
typedef struct pair {
    int key;                   // 哈希表中的键
    char element[20];          // 关联的字符串元素,假设最大长度为 20
} DATA, * LPDATA;

// 定义哈希表结构体
typedef struct hashTable {
    LPDATA* table;             // 存储元素的桶数组,每个桶存储一个 DATA 指针
    int divisor;               // 哈希表的大小,决定了桶的数量
    int curSize;               // 当前哈希表中的元素数量
} HASH, * LPHASH;

// 创建一个哈希表
LPHASH createHashTable(int p) {
    LPHASH hash = (LPHASH)malloc(sizeof(HASH));  // 为哈希表结构体分配内存
    assert(hash != NULL);  // 检查内存分配是否成功

    hash->curSize = 0;                // 初始化哈希表的当前大小为 0
    hash->divisor = p;                // 设置哈希表的大小
    hash->table = (LPDATA*)malloc(sizeof(LPDATA) * hash->divisor);  // 为桶数组分配内存
    assert(hash->table != NULL);  // 检查内存分配是否成功

    // 初始化哈希表中的所有桶指针为 NULL,表示所有桶都是空的
    for (int i = 0; i < hash->divisor; i++) {
        hash->table[i] = NULL;
    }

    return hash;  // 返回创建的哈希表指针
}

// 查找指定 key 的位置
int search(LPHASH hash, int key) {
    int pos = key % hash->divisor;  // 使用哈希函数计算初始位置
    int curpos = pos;               // 当前位置初始化为计算得到的初始位置

    // 线性探测法查找位置,直到找到空位或已有相同 key 的位置
    do {
        if (hash->table[curpos] == NULL || hash->table[curpos]->key == key) {
            return curpos;  // 如果当前位置为空,或者找到了相同的 key,返回当前的位置
        }

        // 如果当前位置被占用且 key 不相同,采用线性探测法,检查下一个位置
        curpos = (curpos + 1) % hash->divisor;  // 如果超出了表的末尾,则环绕到表的开始位置
    } while (curpos != pos);  // 如果回到初始位置,表示表已满

    return -1;  // 如果哈希表已满,返回 -1
}

// 插入数据到哈希表
void insertData(LPHASH hash, DATA data) {
    // 如果哈希表已满,无法插入新数据
    if (hash->curSize >= hash->divisor) {
        printf("Hash table is full!\n");
        return;
    }

    // 查找数据的插入位置
    int pos = search(hash, data.key);

    // 如果返回 -1,表示表已满,无法插入
    if (pos == -1) {
        printf("Hash table is full, cannot insert new data.\n");
        return;
    }

    // 如果该位置为空,插入数据
    if (hash->table[pos] == NULL) {
        hash->table[pos] = (LPDATA)malloc(sizeof(DATA));  // 分配内存存储数据
        assert(hash->table[pos] != NULL);  // 确保内存分配成功

        // 将数据复制到该位置
        memcpy(hash->table[pos], &data, sizeof(DATA));
        hash->curSize++;  // 更新当前哈希表大小
    }
    // 如果该位置已经存在相同的 key,则更新该位置的 element
    else if (hash->table[pos]->key == data.key) {
        // 使用 strncpy 避免字符串溢出,确保 element 的长度不会超过 19 个字符
        strncpy(hash->table[pos]->element, data.element, sizeof(hash->table[pos]->element) - 1);
        hash->table[pos]->element[sizeof(hash->table[pos]->element) - 1] = '\0';  // 确保字符串以 '\0' 结尾
    }
    else {
        printf("Key already exists, but handled by rehashing mechanism.\n");
    }
}

// 打印哈希表的内容
void printHash(LPHASH hash) {
    for (int i = 0; i < hash->divisor; i++) {
        if (hash->table[i] == NULL) {
            printf("NULL\n");  // 如果桶为空,打印 NULL
        }
        else {
            // 打印当前桶中的 key 和 element
            printf("Key:%d Element:%s\n", hash->table[i]->key, hash->table[i]->element);
        }
    }
}

// 主函数
int main() {
    // 创建一个大小为 10 的哈希表
    LPHASH hash = createHashTable(10);

    // 定义一个 DATA 数组,包含 4 个元素
    DATA array[4] = { {1, "雷电"}, {11, "春妙"}, {13, "晓月"}, {17, "雪月"} };

    // 将数组中的数据插入到哈希表中
    for (int i = 0; i < 4; i++) {
        insertData(hash, array[i]);
    }

    // 打印哈希表的内容
    printHash(hash);

    // 释放哈希表中每个桶存储的数据内存
    for (int i = 0; i < hash->divisor; i++) {
        if (hash->table[i] != NULL) {
            free(hash->table[i]);  // 释放每个桶中的 DATA 元素内存
        }
    }

    // 释放存储桶指针数组的内存
    free(hash->table);

    // 释放哈希表结构体本身的内存
    free(hash);

    return 0;  // 程序执行成功,返回 0
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2265270.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

医疗大模型威胁攻击下的医院AI安全:挑战与应对策略

一、引言 1.1 研究背景与意义 随着人工智能技术的迅猛发展,医疗大模型作为一种新兴的技术手段,正逐渐渗透到医疗领域的各个环节,为医疗服务的数字化转型带来了前所未有的机遇。从辅助诊断到疾病预测,从个性化治疗方案的制定到医疗资源的优化配置,医疗大模型展现出了巨大…

如何在谷歌浏览器中使用内置翻译功能

谷歌浏览器作为全球最受欢迎的网络浏览器之一&#xff0c;提供了强大且便捷的内置翻译功能。这一功能帮助用户轻松跨越语言障碍&#xff0c;浏览不同语言的网页内容。本文将详细介绍如何在谷歌浏览器中使用其内置翻译功能。 一、启用谷歌浏览器内置翻译功能 1、打开谷歌浏览器…

【MySQL】7.0 入门学习(七)——MySQL基本指令:帮助、清除输入、查询等

1.0 help &#xff1f; 帮助指令&#xff0c;查询某个指令的解释、用法、说明等。详情参考博文&#xff1a; 【数据库】6.0 MySQL入门学习&#xff08;六&#xff09;——MySQL启动与停止、官方手册、文档查询 https://www.cnblogs.com/xiaofu007/p/10301005.html 2.0 在cmd命…

基于推理的目标检测 DetGPT

基于推理的目标检测 DetGPT flyfish detgpt.github.io 近年来&#xff0c;由于大型语言模型&#xff08;LLMs&#xff09;的发展&#xff0c;计算机视觉领域取得了重大进展。这些模型使人类与机器之间能够进行更有效、更复杂的交互&#xff0c;为模糊人类与机器智能界限的新技…

概率论 期末 笔记

第一章 随机事件及其概率 利用“四大公式”求事件概率 全概率公式与贝叶斯公式 伯努利概型求概率 习题 推导 一维随机变量及其分布 离散型随机变量&#xff08;R.V&#xff09;求分布律 利用常见离散型分布求概率 连续型R.V相关计算 利用常见连续型分布的计算 均匀分布 正态…

探索 Python编程 调试案例:计算小程序中修复偶数的bug

在 学习Python 编程的过程里&#xff0c;会遇到各种各样的bug。而修复bug调试代码就像是一场充满挑战的侦探游戏。每一个隐藏的 bug 都是谜题&#xff0c;等待开发者去揭开真相&#xff0c;让程序可以顺利运行。今天&#xff0c;让我们通过一个实际案例&#xff0c;深入探索 Py…

Redis 介绍和安装

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 Redis 入门介绍 收录于专栏[redis] 本专栏旨在分享学习Linux的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 本章将带领读者进入 Redis 的世…

springboot480基于springboot高校就业招聘系统设计和实现(论文+源码)_kaic

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自古…

【基础篇】1. JasperSoft Studio编辑器与报表属性介绍

编辑器介绍 Jaspersoft Studio有一个多选项卡编辑器&#xff0c;其中包括三个标签&#xff1a;设计&#xff0c;源代码和预览。 Design&#xff1a;报表设计页面&#xff0c;可以图形化拖拉组件设计报表&#xff0c;打开报表文件的主页面Source&#xff1a;源代码页码&#xff…

【河南新标】豫财预〔2024〕105号-《关于省级政务信息化建设项目支出预算标准的规定》-费用标准解读系列29

2024年12月3日&#xff0c;河南省财政厅发布了《关于省级政务信息化建设项目支出预算标准的规定》豫财预〔2024〕105号。《关于省级政务信息化建设项目支出预算标准的规定 &#xff08;试行&#xff09;》&#xff08;豫财预 〔2020〕81号&#xff09;同时废止。新的豫财预〔20…

导入numpy报错:PyCapsule_Import could not import module “datetime“

背景 docker部署深度学习算法时&#xff0c;安装miniconda报错&#xff0c;报线程错误。 然后在构建镜像时把miniconda装进去没有问题。 然后把环境移进去发现报numpy导入错误 在python解释器尝试导入numpy发现还是报错 尝试重新装numpy&#xff0c;发现没有解决。 网上找解决方…

TANGO与LabVIEW控制系统集成

TANGO 是一个开源的设备控制和数据采集框架&#xff0c;主要用于管理实验室设备、自动化系统和工业设备。它为不同类型的硬件提供统一的控制接口&#xff0c;并支持设备之间的通信&#xff0c;广泛应用于粒子加速器、同步辐射光源、实验室自动化和工业控制等领域。 1. TANGO的核…

利用Circuit JS1再学学电子方面的知识(硬件)

1 电阻器 1.1 电阻并联 每个电阻电压相同&#xff0c;总电流等于各支路电流之和。 并联电阻值 R 1/(1/R11/R2);R约等于90.9 电阻并联后的阻值比最小的一个电阻值都小。 1.2 电阻串联 电阻串联的阻值为各电阻阻值相加。 RR1R2&#xff0c;串联涉及电阻分压。 一般在开关处…

使用Amazon Bedrock的无服务器的智能工作流

使用Amazon Bedrock的无服务器的智能工作流 智能工作流基于用户输入处理不可预见的任务&#xff0c;比如发起API调用。无服务器架构可以高效地管理这些任务和不同的工作负载&#xff0c;而无需维护服务器&#xff0c;从而加快部署速度。 你将学习如何通过使用带有保护措施的智能…

国高材服务 | 高分子结晶动力学表征——高低温热台偏光显微镜

众所周知&#xff0c;聚合物制品的实际使用性能&#xff08;如光学透明性、硬度、模量等&#xff09;与材料内部的结晶形态、晶粒大小及完善程度有着密切的联系&#xff0c;因此&#xff0c;对聚合物结晶形态等的研究具有重要的理论和实际意义。 随着结晶条件的不用&#xff0c…

【LeetCode每日一题】——415.字符串相加

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时空频度】九【代码实现】十【提交结果】 一【题目类别】 字符串 二【题目难度】 简单 三【题目编号】 415.字符串相加 四【题目描述】 给定两个字符…

idea设置控制台日志输出自动换行

文章目录 1. 原因2. 方法一&#xff1a;3. 方法二&#xff1a; 1. 原因 你是否碰到ideal控制台输入日志是一行的效果&#xff0c;那是因为带了soft wrap。 2. 方法一&#xff1a; 最新版的IDEA设置控制台自动换行位置如下&#xff1a; Setting->Editor->General->C…

探索多模态大语言模型(MLLMs)的推理能力

探索多模态大语言模型&#xff08;MLLMs&#xff09;的推理能力 Multimodal Large Language Models (MLLMs) flyfish 原文&#xff1a;Exploring the Reasoning Abilities of Multimodal Large Language Models (MLLMs): A Comprehensive Survey on Emerging Trends in Mult…

如何查看pad的console输出,以便我们更好的进行调试,查看并了解实际可能的问题。

1、以下是baidu AI回复&#xff1a; 2、说明&#xff1a; 1&#xff09;如果小伙伴们经常做android开发的话&#xff0c;这个不陌生&#xff0c;因为调试都是要开启这个开发者模式。并启用USB调试模式。 2&#xff09;需要连上USB线&#xff0c;有的时候会忘记&#xff0c;然…

什么是CRM软件?CRM软件在企业中扮演什么角色?

什么是CRM软件&#xff1f; 嘿&#xff0c;大家好&#xff01;今天咱们来聊聊一个超级重要的工具——CRM软件。你知道吗&#xff1f;CRM其实是Customer Relationship Management的缩写&#xff0c;翻译过来就是客户关系管理。这玩意儿听起来好像很复杂&#xff0c;其实它就是一…