数据结构基础详解:哈希表【C语言代码实践篇】开放地址法__拉链法_哈希表的创建_增删查操作详解

news2024/9/27 12:17:13

文章目录

  • 1.哈希表代码实现之开放地址法
    • 1.1 开放地址法创建哈希表
    • 1.2 开放地址法之查找
    • 1.3 开放地址法之插入
    • 1.4 开放地址法之删除
  • 2.哈希表代码实现之链地址法(拉链法)
    • 2.1 链地址法之创建哈希表
    • 2.2 链地址法之查找
    • 2.3 链地址法之插入
    • 2.4 链地址法之删除

1.哈希表代码实现之开放地址法

1.1 开放地址法创建哈希表

哈希表本质就是一个线性表,定义一个哈希表结构体,包括一个动态数组PList,表长,和关键字个数(元素个数)
代码实现的一些细节
1.没有关键字的地方,默认初始值要设置成99999(就是无穷大),因为动态设置一个数组是随机值,会影响到代码结果

//开放地址法哈希表的创建
# define INF 999999999;
typedef int ElemType;

typedef struct HashTable
{
    int kNum;
    ElemType *pList;
    int tLength;
}HashTable;

void initial(HashTable &HT,int tlength)
{
    HT.pList=(ElemType *)malloc(sizeof(HashTable)*tlength);
    HT.tLength=tlength;
    for(int i=0;i<tlength;i++)
    {
        HT.pList[i]=INF;
    }
    HT.kNum=0;
}

HashTable creatHT(ElemType tlength)
{
    HashTable HT;
    initial(HT,tlength);
    return HT;
}

1.2 开放地址法之查找

在这里插入图片描述

构建一个如上图所示的哈希表作为样例:

int main()
{
    HashTable HT=creatHT(10);
    getDi(HT.tLength);
    HT.pList[6]=6;
    HT.pList[7]=13;
    HT.pList[8]=27;
    HT.pList[9]=41;
    HT.pList[0]=55;
    HT.tLength=10;
    
    int ret=search(54, HT);
    printf("%d\n",ret);
}

具体查找代码的实现:

#define P 7
int Hash(ElemType key)  //除留余数法哈希函数
{
    return key%P;
}

int Di[100]={0};
void getDi(int tLength) //初始化一个线性探测序列,0,1,2,3,4,5,6,.....
{
    for(int i=1;i<=tLength-1;i++) //为什么是tLength-1,因为假如表长为10,地址空间是0-9
    {
        Di[i]=i;
    }
}

int isUpperBound(int di,int tLength) //判断边界是否超过,意味着若超出边界,则哈希表中没有该元素
{
    if(di>tLength-1) //是否超出查找范围
    {
        return 0;  //超出查找范围
    }
    return 1;
}

int search(ElemType key,HashTable HT)  //给出要查的关键字和哈希表,进行查找
{
    //初始化查找
    int i=0;
    int Hi=(Di[i]+Hash(key))%HT.tLength;   //线性探测法函数的构建,除的是表长
    
    //如果没有超出界限,并且没有查到空白的元素,就一直找到超出界限为止
    while (isUpperBound(Di[i], HT.tLength)&&HT.pList[Hi]!=INF){
        if(HT.pList[Hi]==key)return 1;  //找到了
        
        i++;
        Hi=(Di[i]+Hash(key))%HT.tLength;
    }
    return 0

1.3 开放地址法之插入

开放地址的插入其实就是在查找操作上进行了改进,在查找中,多引入一个pos指针,pos指针返回待插入位置或是当前哈希表已经满了,pos就返回最后一个元素地址。

查找操作的修改代码:

int search(ElemType key,HashTable HT,int &pos)  //给出要查的关键字和哈希表,进行查找
{
    //初始化查找
    int i=0;
    int Hi=(Di[i]+Hash(key))%HT.tLength;   //线性探测法函数的构建,除的是表长
    
    //如果没有超出界限,并且没有查到空白的元素,就一直找到超出界限为止
    while (isUpperBound(Di[i], HT.tLength)&&HT.pList[Hi]!=INF){
        if(HT.pList[Hi]==key)return 1;  //找到了
        
        i++;
        Hi=(Di[i]+Hash(key))%HT.tLength;
    }
    pos=Hi;  //这个位置可能是空白的存储单元,也可能是最后一个访问的关键字位置
    return 0;
}

插入代码的实现:

int insrt(ElemType key,HashTable &HT)
{
    int pos=-1;
    int ret=search(key, HT, pos); //拿到pos
    if(ret==0&&HT.pList[pos]==INF) //ret==0意味着,我们要插入的元素,原哈希表中并不存在
    {
        HT.pList[pos]=key;
        HT.kNum++;
        return 1;  //插入成功
    }
    return 0;  //插入失败
}

测试代码:

int main()
{
    HashTable HT=creatHT(10);
    getDi(HT.tLength);
    HT.pList[6]=6; HT.pList[7]=13;HT.pList[8]=27;
    HT.pList[9]=41;HT.pList[0]=55;
    HT.tLength=10;
    
    int ret=insrt(57, HT);
    for (int i=0; i<HT.tLength; i++) {
        printf("%d\n",HT.pList[i]);
    }
}

1.4 开放地址法之删除

删除操作,本质上也是在查找操作的基础上修改
找到要删除元素的位置,将那个位置的值设置为无穷大,并统计表中元素-1

修改后的查找函数:

int delete_serch(ElemType key,HashTable HT,int &pos)
{
    //初始化查找
    int i=0;
    int Hi=(Di[i]+Hash(key))%HT.tLength;   //线性探测法函数的构建,除的是表长
    
    //如果没有超出界限,并且没有查到空白的元素,就一直找到超出界限为止
    while (isUpperBound(Di[i], HT.tLength)&&HT.pList[Hi]!=INF){
        if(HT.pList[Hi]==key)
        {
            pos=Hi;
            return 1;  //找到了
        }
        i++;
        Hi=(Di[i]+Hash(key))%HT.tLength;
    }
    return 0;
}

删除操作:

int detele_hash(ElemType key,HashTable &HT)
{
    int pos=-1;
    if(delete_serch(key, HT, pos)==1)
    {
        HT.pList[pos]=INF;
    }
    return 0;
}

2.哈希表代码实现之链地址法(拉链法)

在这里插入图片描述

把这个拉链法,分成两部分,右边,就看成多条链表。
左边存储的是指针,是指针数组,也就是存储的它挂着的那些链的第一个结点
pList是指向指针数组的指针,是指针的指针

2.1 链地址法之创建哈希表

typedef struct Node
{
    ElemType key;
    struct Node * next;
    
}Node;


typedef struct ChHashTable
{
    Node **pList;  //指向指针数组的指针
    int tlength;  //哈希表长度
    int kNum;  //关键字的个数
}ChHashTable;


void initial(ChHashTable &CHT,int tLength)
{
    CHT.pList=(struct Node**)malloc(sizeof(struct node *)*tLength); //分配动态数组
    CHT.tlength=tLength;
    for(int i=0;i<tLength;i++)
    {
        CHT.pList[i]=NULL;
    }
    CHT.kNum=0;
}
ChHashTable creat(int tLength)
{
    ChHashTable CHT;
    initial(CHT, tLength);
    return CHT;
}

2.2 链地址法之查找

链地址法的查找和插入基本上一样,这里省略,插入不省略

2.3 链地址法之插入

插入代码如下:

//链地址的插入其实就是单链表的插入,这里用尾插法进行链地址哈希表的插入
void insrt(ElemType key,ChHashTable &CHT)
{
    int i=Hash(key);  //找到待插入的数组下标
    
    Node *pCur=CHT.pList[i]; //获取当前数组下标的第一个元素,可能空的,也可能非空,就是存储的是第一个链表的地址
    Node * preNode=NULL;
    
    if(pCur==NULL)   //如果它是空的
    {
        struct Node * pNode=(Node *)malloc(sizeof(Node));
        pNode->key=key;
        pNode->next=NULL;
        CHT.pList[i]=pNode;
    }else
    {
        //如果它非空,就不断的查找,如果查到了就不插入,查不到就用尾插法插入
        while(pCur!=NULL)
        {
            if(pCur->key==key) break;  //不插入了
            
            preNode=pCur;
            pCur=pCur->next;
            
            if(pCur==NULL)  //没有找到待插入的元素,用尾插法插入
            {
                struct Node * pNode=(Node *)malloc(sizeof(Node));
                pNode->key=key;
                pNode->next=NULL;
               
                preNode->next=pNode;
            }
        }
    }
}

测试代码如下:

int main()
{
    ChHashTable CHT=creat(10);
    
    ElemType keyList[]={31,23,17,27,19,11,13,91,61,41};
    int keyListLength=10;
    
    for(int i=0;i<keyListLength;i++)
    {
        insrt(keyList[i], CHT);
    }
    
  //  int dd=delt(31, CHT);  删除测试
   // int dd1=delt(11, CHT);
    //int dd2=delt(13, CHT);
    
    for(int i=0;i<CHT.tlength;i++)
    {
        Node *pCur=CHT.pList[i];
        while(pCur!=NULL)
        {
            printf("%d ",pCur->key);
            pCur=pCur->next;
        }
        printf("\n");
    }

在这里插入图片描述

2.4 链地址法之删除

int delt(ElemType key,ChHashTable &CHT)
{
    int i=Hash(key);  //找到待插入的数组下标
    
    Node *pCur=CHT.pList[i]; //获取当前数组下标的第一个元素,可能空的,也可能非空,就是存储的是第一个链表的地址
    Node * preNode=NULL;
    
    //在删除操作中,需要分为两种情况,第一种情况,是第一个结点,要在指针数组上操作,不是第一个结点
    
    if(pCur==NULL)
    {
        return 0;
    }else   //在不为空的情况下,删除
    {
        if(pCur->key==key)
        {
            CHT.pList[i]=pCur->next;
            free(pCur);
            return 1;
        }
        else
        {
            while(pCur!=NULL)  //一直找
            {
                if(pCur->key==key)
                {
                    preNode->next=pCur->next;
                    free(pCur);
                    break;
                }
                preNode=pCur;
                pCur=pCur->next;
            }
        }
    }
    return 0;
}

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

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

相关文章

Stable diffusion生图原理

简介 Stable diffusion 是一种基于扩散技术的深度学习模型&#xff0c;于2022年发布&#xff0c;是Stability AI公司推出的首要产品&#xff0c;它主要用于生成以文本描述为条件的详细图像&#xff0c;同时也可以进行补绘、外绘、重绘等任务&#xff0c;但原理都和文生图原理…

C++中矩阵的介绍及相关应用扩展详解

1. 矩阵概念 在数学中&#xff0c;矩阵&#xff08;Matrix&#xff09;是一个按照长方阵列排列的复数或实数集合&#xff0c;最早来自于方程组的系数及常数所构成的方阵。这一概念由19世纪英国数学家凯利首先提出。 矩阵是高等代数学中的常见工具&#xff0c;也常见于统计分析…

Qt-QPushButton按钮类控件(22)

目录 描述 使用 给按钮添加图片 给按钮添加快捷键 添加槽函数 添加快捷键 添加组合键 开启鼠标的连发功能 描述 经过上面的一些介绍&#xff0c;我们也尝试的使用过了这个控件&#xff0c;接下来我们就要详细介绍这些比较重要的控件了 使用 给按钮添加图片 我们创建…

线性表之单链表

在上一节我们学习了线性表中的顺序表&#xff0c;今天我们来学习一下线性表中的另一种结构——单链表 前言 我们在之前已经初步了解了数据结构中的两种逻辑结构&#xff0c;但线性结构中并非只有顺序表一种&#xff0c;它还有不少兄弟姐妹&#xff0c;今天我们再来学习一下单链…

RealityCapture全面讲解:摄影测量软件的新纪元

随着数字化技术的迅猛发展&#xff0c;摄影测量软件在各行各业中的应用日益广泛。其中&#xff0c;RealityCapture作为一款领先的摄影测量解决方案&#xff0c;以其卓越的速度、精度和易用性&#xff0c;赢得了全球众多专业人士的青睐。本文将全面讲解RealityCapture的功能特点…

演示:基于WPF自绘的中国省份、城市、区县矢量地图

一、目的&#xff1a;演示一个基于WPF自绘的中国省份、城市、区县矢量地图 二、效果 国 省 市 三、功能 支持实际经纬度显示 支持平移&#xff0c;缩放等功能 显示中国地图 显示各个省份地图 显示各个省份地图&#xff08;包含在表格中&#xff0c;包含缩率图&#xff09; 显…

UE4_后期处理五—饱和度调整、隔离、扭曲、重影

一、色彩饱和度调整&#xff1a; 原图 后期处理材质节点&#xff1a; 效果图&#xff1a; 可以根据参数saturation调整饱和还是去饱和。 当saturation为1时&#xff1a;去饱和度&#xff0c;如下图&#xff1a; 当saturation为0时&#xff1a;原始的一个状态&#xff0c;如下…

JS import export export default ES6 modules 玩的明白吗

export (ES6) 导出 一个文件可以有多个&#xff0c;不可重名 命名导出&#xff1a; 使用export关键字导出变量、函数、类或值时&#xff0c;需要为它们指定名称。这些名称将在其他模块中用于导入。 export default 单一导出&#xff1a; export default 只能用于导出一个模块、…

python 读取excel

一、安装依赖&#xff1a; pandas 二、新建excel 示例数据&#xff1a;students.xlsx 三、定义类&#xff1a;student.py Student class Student:def __init__(self, name, sex):self.name nameself.sex sexdef show(self):print(f姓名&#xff1a;{self.name} 性别&#…

全面理解tensor编程中矩阵的行和列

经常会在编程中遇到理解矩阵行和列的事情。 1、要明确无论这个张量有多少维度&#xff0c;它的矩阵乘法都只能作用于最后两个维度。 例如&#xff1a; import torcha torch.rand([64, 32, 3, 4]) b torch.rand([64, 32, 3, 4])c torch.matmul(a, b.transpose(2, 3)) # 交…

3.接口测试的基础/接口关联(Jmeter工具/场景一:我一个人负责所有的接口,项目规模不大)

一、Jmeter接口测试实战 1.场景一&#xff1a;我一个人负责所有的接口&#xff1a;项目规模不大 http:80 https:443 接口文档一般是开发给的&#xff0c;如果没有那就需要抓包。 请求默认值&#xff1a; 2.请求&#xff1a; 请求方式:get,post 请求路径 请求参数 查询字符串参数…

sh文件执行提示语法错误: 未预期的文件结尾

在执行sh文件时总是提示&#xff1a;语法错误: 未预期的文件结尾&#xff0c;尝试删除最后的空格也不对 最后发现在notepad中转换的问题 需要把windows换成unix就行了

时间序列中的多尺度问题-近期值得关注的8篇多尺度建模工作

时间序列的多尺度建模 多尺度是时序研究必须要考虑的问题。一方面&#xff0c;不同特征的周期模式有长有短&#xff0c;需要用不同尺度进行刻画。另一方面&#xff0c;尺度越小越精细&#xff0c;计算越复杂&#xff1b;尺度越大越粗糙&#xff0c;相应计算量减少&#xff0c;…

容器化安装jenkins稳定版长期维护版本LTS

前提已有 docker-compose和docker-ce环境&#xff0c;这里安装稳定的Lts版本即可。 选择稳定版本 这里选择LTS 稳定长期维护的版本 在docker镜像找到LTS稳定版本 部署jenkins服务 创建持久化数据目录 jenkinsdata]# pwd /data/jenkinsdata编写docker-compose文件 jenkins_…

DAY 13 : 排序

定义 稳定排序和非稳定排序 设文件f&#xff08;R1……Ri……Rj……Rn&#xff09;中记录Ri、Rj&#xff08;i≠j&#xff0c;i、j1……n&#xff09;的key相等&#xff0c;即KiKj。 若在排序前Ri领先于Rj&#xff0c;排序后Ri仍领先于Rj&#xff0c;则称这种排序是稳定的&…

Linux操作系统入门(三)

_______________________________________________ 一.Linux操作系统的文件结构 相比于Windows操作系统的C,D,E等盘符&#xff0c;Linux操作系统仅有一个"/"符号的根目录. 这其中存在一个显著的不同&#xff0c;Linux操作系统使用的是斜杠"/",而Windows…

【LLM多模态】文生视频评测基准VBench

note VBench的16个维度自动化评估指标代码实践&#xff08;待完成&#xff09;16个维度的prompt举例人类偏好标注&#xff1a;计算VBench评估结果与人类偏好之间的相关性、用于DPO微调 文章目录 note一、相关背景二、VBench评测基准概述&#xff1a;论文如何解决这个问题&…

AJAX 入门 day1

目录 1.AJAX 概念和 axios 使用 2.认识 URL 3.URL 查询参数 4.常用请求方法和数据提交 5.HTTP协议-报文 5.1 HTTP 协议&#xff0d;请求报文 5.2 HTTP 协议&#xff0d;响应报文 6.接口文档 7.案例 - 用户登录 8.form-serialize 插件 1.AJAX 概念和 axios 使用 “Aj…

华为OD机试 - 找出作弊的人(Java 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;E卷D卷A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加…

JNI 详细介绍

一 介绍 java调⽤c&#xff0c;c代码可以通过JNIEnv执行java代码。 安卓NDK 已经对JNI环境进行了集成&#xff0c;我们可以通过android studio来快速搭建一个项目。 二 项目搭建 打开android studio 创建工程&#xff0c;创建工程选择模板Native C 三 模板格式介绍 生成的…