数据结构之哈希表以及常用哈希的算法表达(含全部代码)

简介: 数据结构之哈希表以及常用哈希的算法表达(含全部代码)

目录


为什么要有哈希

哈希表

含义

创建哈希表需要注意的点

算法的选择

哈希冲突的处理

线性探测法

再哈希法

链表法

哈希表的实现(代码部分)

确定结构体(节点)

准备一个哈希算法

创建一个哈希表(即开辟空间)

创建节点

数据加入哈希表的具体实现

获取数据,数据加入哈希表

打印哈希表

查找哈希表(重点,这也能帮你理解哈希表的结构与优势所在)

销毁哈希表

主函数


正文


为什么要有哈希


如果要存储一组数据,我们都能想到使用数组或者链表来存储,但是实际生产生活中,数据都是非常庞大的,动辄几十G,几十T,甚至以P来计算。这样一来,数组和链表的优势以劣势就非常明显了。

插入数据 查找数据
数组 劣势 优势
链表 优势 劣势

既然这两种方法都不能完美解决实际问题,那就一定会有一种方法的诞生来处理它,我们的哈希表就这样诞生啦,哈希表集这两种存储形式的优点于一身,接下来就让我们走进哈希表吧。


哈希表


含义


哈希表也叫散列表,哈希表是一种数据结构,它提供了快速的插入操作和查找操作,无论哈希表总中有多少条数据,在有足够优秀的哈希算法时,插入和查找的时间复杂度都是为O(1)。


散列表 ( Hash table  ),是根据键(Key)而直接访问在记忆体储存位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表 。表中存储的是一个地址,我们通过键值计算出来的哈希值得到这个地址,再取这个地址中的数据即可。


创建哈希表需要注意的点


算法的选择


常见的算法形式有,根据键值进行加法运算,减法运算,乘法运算,位移运算。


值得一说的是位移运算,无论是前移还是后移, 都会使键值转换出来的哈希值大小难以确定,可能有键值很大,但是哈希值很小的情况,这就让使用者无法大致确定键值对应的哈希值。但这并不是缺点,看实际生产要求。


而一般都是以上几种运算的复合运算作为哈希算法。例如;

hashvalue = (hashvalue << 4) + hashvalue + *key++;
//hashvalue为哈希值,key为键值


哈希冲突的处理


哈希冲突即为不同键值通过哈希算法得到了相同的哈希值,这种情况,我们肯定不能将数据直接存储到对应的地址去,这样会将原来的数据覆盖。对此常见的 方法有一下三种,我们最推荐使用链表的方式。


线性探测法


在线性探测哈希表中,数据的插入是线性的查找空白单元,例如我们将数88经过哈希函数后得到的数组下标是16,但是在数组下标为16的地方已经存在元素,那么就找17,17还存在元素就找18,一直往下找,直到找到空白地方存放元素。


再哈希法


双哈希是为了消除原始聚集和二次聚集问题,线性探测每次的探测步长都是固定的。双哈希是除了第一个哈希函数外再增加一个哈希函数用来根据关键字生成探测步长,这样即使第一个哈希函数映射到了数组的同一下标,但是探测步长不一样,这样就能够解决聚集的问题。

和第一个哈希函数不一样

不能输出为0,因为步长为0,每次探测都是指向同一个位置,将进入死循环,经过试验得出stepSize = constant-(key%constant);形式的哈希函数效果非常好,constant是一个质数并且小于数组容量


链表法


通过在哈希表中再寻找一个空位解决冲突的问题,还有一种更加常用的办法是使用「链表法」来解决哈希冲突。「链表法」相对简单很多,「链表法」是每个数组对应一条链表。当某项关键字通过哈希后落到哈希表中的某个位置,把该条数据添加到链表中,其他同样映射到这个位置的数据项也只需要添加到链表中,并不需要在原始数组中寻找空位来存储。

345.png


哈希表的实现(代码部分)


确定结构体(节点)


#define HASHSIZE 256 //你的哈希表的元素个数
typedef int mydatatype;
typedef unsigned char uint8;
struct node
{
  //char * key;//键值,最好使用指针,这里为了便捷,使用数组
  char key[128];
  mydatatype data;//这个东西就是你的数据
  struct node * next;    //解决哈希冲突的链表法
};


准备一个哈希算法


/* 
GetHashValue:获取哈希值
  @key:输入的键值
  返回值:
    输入的键值对应的哈希值
 */
uint8 GetHashValue(const char * key)
{
  uint8 hashvalue = 0;
  while(*key)
  {
    hashvalue = (hashvalue << 4) + hashvalue + *key++;  //首先我们应该准备一个哈希算法
                                                            //你可能会要调试多次
  }
  printf("hashvalue = %d\n",hashvalue);
  return hashvalue; 
}


创建一个哈希表(即开辟空间)


/* 
GetHashTable: 得到一个哈希表
  返回值:
    哈希表所需的空间
 */
struct node ** GetHashTable(void)
{
            //这个空间开出来就是为了保存地址的  
            //因此sizeof(struct node *)
  return (struct node **)calloc(sizeof(struct node *) , HASHSIZE);    //calloc会初始化空间为0
}


创建节点


/* 
GetDataNode: 创建节点的函数
  @key:输入的键值
  @data:键值对应的数据
  返回值:
    哈希表的一个节点
 */
static struct node * GetDataNode(const char * key,mydatatype data)  //const关键字对变量加以限定,使它不能被改变
{
  struct node * pnew = malloc(sizeof(struct node));
    strcpy(pnew ->key,key);   //注意,这里不能直接写pnew->key = key;
    pnew ->data = data;
    pmew ->next = NULL;    
  return pnew;
}


数据加入哈希表的具体实现


/* 
InsertDataToHashTable:将数据加入到哈希表
  @ht:哈希表
  @key:键值
  @date:键值对应的数值
 */
static void InsertDataToHashTable(struct node ** ht,const char * key,mydatatype data)
{
    struct node *pnew = GetDataNode(key,data);
    //计算哈希值
    uint8 hashvalue = GethashValue(key);
    if(!ht[hashvalue])  //如果哈希表的下标为hashvalue的元素为空 
              //则这个地方是没有填入数据的 直接填入就可以了
    {
        ht[hashvalue]) = pnew;
    }
    else
    {
        //如果有hashvlaue与key值d都相同的情况 
    //一般有两种做法  1 直接丢弃不用   2 加上一个num计数
    //一般我们用尾插法插入
        struct node * p = ht[hashvalue];
        while(p)
        {
            if(!strcmp(p ->key,key))
            {
                free(pnew);
                printf("键值一样\n");
                return;
            }
            p = p->next;
        }
        p = pnew;    //p已经是最后一个节点的next
    }
}


获取数据,数据加入哈希表


/* 
GetData:获取数据,加入到哈希表中
  @ht:哈希表
 */
void GetData(struct node ** ht)
{
    if(!ht)
        return;
    mydatatype data;
    char key[128] = {0};
    while(1)
    {
        //先输入键值,再输入data
        if(scanf("%s%d",key,data) != 2)//你匹配上的东西不等于2  说明你的匹配失败
        {
            char ha[1024];
            fgets(ha,1024,stdin);    //将标准输入里面的东西拿掉
            printf("输入有误,请重新输入\n");
`            continue;
        }    
        if(!strncmp(key,"##",2))    //输入“##”结束输入
            break;
        InsertDataToHashTable(ht,key,data);
    }
}


打印哈希表


/* 
PrintHashTable:打印这个哈希表
  @ht:哈希表
 */
void PrintHashTable(struct node ** ht)
{
    int i;
    for(i = 0;i < HASHSIZE;i++)
    {
        if(ht[i])
        {
            struct node *p = hr[i];
            while(p)
            {
                printf("HV = %d\tkey = %s\tdata = %d\n",i,p->key,p->data);
                p = p ->next;
            }
        }
    }   
}


查找哈希表(重点,这也能帮你理解哈希表的结构与优势所在)


/* 
FindData:查找哈希表
  @ht:哈希表
  @key:键值
 */
void FindData(struct node ** ht,const char * key)
{
    uint8 hashvalue = GetHashValue(key);
    if(ht[hashvalue])    //哈希表存在
    {
        struct node * p = ht[hashvalue];
        int i = 1;
        while(p)
        {
            if(strcmp(p ->key,key)
            {
                printf("找到了,键值为%d中的第%d个,value = %d\n",hashvalue,i,p->data);
                return;
            }
            i++;
            p = p ->next;
        }
    }
    printf("查找的元素%s不存在\n",key);
}


销毁哈希表


/* 
DestoryHashTable:销毁这个哈希表
  @ht:哈希表
 */
void DestoryHashTable(struct node ** ht)
{
    int i = 0; 
    for(i = 0;i < HASHSIZE;i++)
    {
        struct node * p = ht[i];
        struct node * pre = NULL;
        while(!ht[i])
        {
            if(p ->next)
            {
                free(p);
                pre ->next = NULL;
                p = ht[i];
            }
            else
            {
                pre = p;
                p = p ->next;
            }
        }
    }
}


主函数


int main()
{
  struct node ** ht = GetHashTable();
  GetData(ht);
  PrintHashTable(ht);
  printf("输入你要查找的键值:");
  char buf[128] = {0};
  scanf("%s",buf);
  FindData(ht,buf);
  DestoryHashTable(ht);
  return 0;
}
相关文章
|
8天前
|
存储 监控 算法
电脑监控管理中的 C# 哈希表进程资源索引算法
哈希表凭借O(1)查询效率、动态增删性能及低内存开销,适配电脑监控系统对进程资源数据的实时索引需求。通过定制哈希函数与链地址法冲突解决,实现高效进程状态追踪与异常预警。
78 10
|
11天前
|
存储 算法 安全
控制局域网电脑上网的 PHP 哈希表 IP 黑名单过滤算法
本文设计基于哈希表的IP黑名单过滤算法,利用O(1)快速查找特性,实现局域网电脑上网的高效管控。通过PHP关联数组构建黑名单,支持实时拦截、动态增删与自动过期清理,适用于50-500台终端场景,显著降低网络延迟,提升管控灵活性与响应速度。
52 8
|
9天前
|
机器学习/深度学习 算法 机器人
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
|
9天前
|
机器学习/深度学习 算法 机器人
使用哈里斯角Harris和SIFT算法来实现局部特征匹配(Matlab代码实现)
使用哈里斯角Harris和SIFT算法来实现局部特征匹配(Matlab代码实现)
|
9天前
|
机器学习/深度学习 算法 自动驾驶
基于导向滤波的暗通道去雾算法在灰度与彩色图像可见度复原中的研究(Matlab代码实现)
基于导向滤波的暗通道去雾算法在灰度与彩色图像可见度复原中的研究(Matlab代码实现)
|
9天前
|
机器学习/深度学习 数据采集 负载均衡
结合多种启发式解码方法的混合多目标进化算法,用于解决带工人约束的混合流水车间调度问题(Matlab代码实现)
结合多种启发式解码方法的混合多目标进化算法,用于解决带工人约束的混合流水车间调度问题(Matlab代码实现)
|
9天前
|
机器学习/深度学习 人工智能 算法
【基于TTNRBO优化DBN回归预测】基于瞬态三角牛顿-拉夫逊优化算法(TTNRBO)优化深度信念网络(DBN)数据回归预测研究(Matlab代码实现)
【基于TTNRBO优化DBN回归预测】基于瞬态三角牛顿-拉夫逊优化算法(TTNRBO)优化深度信念网络(DBN)数据回归预测研究(Matlab代码实现)
|
2月前
|
机器学习/深度学习 传感器 算法
【无人车路径跟踪】基于神经网络的数据驱动迭代学习控制(ILC)算法,用于具有未知模型和重复任务的非线性单输入单输出(SISO)离散时间系统的无人车的路径跟踪(Matlab代码实现)
【无人车路径跟踪】基于神经网络的数据驱动迭代学习控制(ILC)算法,用于具有未知模型和重复任务的非线性单输入单输出(SISO)离散时间系统的无人车的路径跟踪(Matlab代码实现)
117 2
|
2月前
|
canal 算法 vr&ar
【图像处理】基于电磁学优化算法的多阈值分割算法研究(Matlab代码实现)
【图像处理】基于电磁学优化算法的多阈值分割算法研究(Matlab代码实现)
|
16天前
|
存储 运维 监控
局域网网络监控软件的设备连接日志哈希表 C++ 语言算法
针对局域网监控软件日志查询效率低的问题,采用哈希表优化设备连接日志管理。通过IP哈希映射实现O(1)级增删查操作,结合链地址法解决冲突,显著提升500+设备环境下的实时处理性能,内存占用低且易于扩展,有效支撑高并发日志操作。
94 0
下一篇
开通oss服务