构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。说到哈希表,,首先就得说到哈希函数,哈希函数是用来得到给定key值的在哈希表中的存储位置的。哈希函数也并不是固定的,可以自己根据情况来定,一般常用常见的有直接定制法,除留余数法,平方取中法,折叠法,随机数法,数学分析法。当向该结构插入元素时,存入根据关键码以此函数计算出的位置,当搜索时,也是先要将给定的关键码用函数转换成存储位置进行查找,将得到位置处的元素进行比较,若关键码相同,则搜索成功。但是通过一个哈希函数得到的位置,一定是会有冲突的,例如用除留余数法,哈希函数为key/100。在此情况下数字1与数字101得到的存储位置就是相同的,这样就是哈希冲突,  哈希冲突一般有两种解决方式,一种是闭散列,另一种是开散列。闭散列(开放地址法):当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中还有空位,那就可以把key值存放到了列表的下一个空位。开散列(链地址法):首先对关键码集合用哈希函数计算哈希表中的偏移位置,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

以下是闭散列实现的哈希表:
    闭散列就是用哈希函数将每个值的所对应的下标算出,将每一个元素放入哈希表对应的下标元素中,若出现冲突则将当前的值放入算出的位置的下一个空位中。
    例如下图中,假设使用的哈希函数是用除留余数法,用需要插入的数组为arr={1,2,101,102,6},哈希表的最大长度为100,则1对应的下标就为1,2的下标就是2,这时出现了101,101的位置算出来也是1,这样它就与1发生了哈希冲突,101就需要继续向后寻找空位,找到下标为3的地方,只要插入的元素数量没有超过哈希表的负载,就能一直往哈希表中插入元素,如果当找到最后一个元素还没有找到空位,则需要将其下标设置为1,再继续进行寻找知道找到第一个空位。

哈希表的头文件:

#pragma once
#define HashMaxSize 1000    //宏定义哈希表的最大容量
#define LoadFactor 0.8       //宏定义负载因子,用于表示哈希表的负载能力。typedef int KeyType;
typedef int ValueType;
typedef size_t(*HashFunc)(KeyType key);     //重定义哈希函数typedef enum Stat     //用于表示每个元素的状态
{Empty,     //空,当前没有值Valid,     //有效,当前的值有效Invalid    //非空但无效,表示当前结点被删除
}Stat;typedef struct HashElem      //哈希表的元素结构体
{KeyType key;ValueType value;Stat stat;
}HashElem;typedef struct HashTable              //哈希表
{HashElem data[HashMaxSize];size_t size;                 //当前有效的元素个数HashFunc hashfunc;
}HashTable;///////////////////////////哈希表的相关操作///////////////////////////////////////
void HashTableInit(HashTable* ht,HashFunc hashfunc);int HashTableInsert(HashTable* ht, KeyType key, ValueType value);//哈希表的查找,找到返回1,并返回这个结点的value值,未找到返回0
int HashTableFind(HashTable* ht, KeyType key,ValueType* value,size_t* cur);//删除值为key的结点
void HashRemove(HashTable* ht, KeyType key);///判断哈希表是否为空
int HashEmpty(HashTable* ht);//求哈希表的大小
size_t HashSize(HashTable* ht);//销毁哈希表
void HashTableDestroy(HashTable* ht);

下面是对各类功能的实现:

size_t HashFuncDefault(KeyType key)
{return key%HashMaxSize;
}
////////////////////////////////////////////////////////////////////////////////////
void HashTableInit(HashTable* ht)
{if (ht == NULL)    //非法输入return;ht->size = 0;ht->hashfunc = HashFuncDefault;for (size_t i = 0; i < HashMaxSize; i++){ht->data[i].key = 0;ht->data[i].stat = Empty;ht->data[i].value = 0;}
}
//哈希表的插入 ,插入成功返回1,插入失败返回0
int HashTableInsert(HashTable* ht, KeyType key, ValueType value)
{if (ht == NULL)return 0;if (ht->size >= HashMaxSize*LoadFactor)   //当哈希表的size超出了负载return 0;//1.先根据哈希函数将key转换,求得key在哈希表中的下标size_t cur = ht->hashfunc(key);//2.判断当前下标是否被占用while (1){if (ht->data[cur].key == key)     //用于保证不会用重复的数字存入哈希表return 0;if (ht->data[cur].stat != Valid){ht->data[cur].key = key;ht->data[cur].value = value;ht->data[cur].stat = Valid;ht->size++;return 1;}cur++;}
}//哈希表的查找 找到返回1,没找到返回0
int HashTableFind(HashTable* ht, KeyType key, ValueType* value)
{if (ht == NULL)return 0;//通过哈希函数找到key所对应的下标size_t offset=ht->hashfunc(key);//若当前下标所对应的值正好是key并且当前的状态必须为valid才返回if (ht->data[offset].key == key&&ht->data[offset].stat==Valid){*value = ht->data[offset].value;return 1;}//若当前下标所对应的值不是key,则继续向后进行查找,直到找到stat等于emptyelse{while (ht->data[offset].stat != Empty){if (ht->data[offset].key != key){offset++;//判断是否下标已超出最大值if (offset >= HashMaxSize)offset = 0;}else{if (ht->data[offset].stat == Valid){*value = ht->data[offset].value;return 1;}elseoffset++;}}return 0;}
}
//删除节点
int HashTableFindCur(HashTable* ht, KeyType key, size_t* cur)
{if (ht == NULL)return 0;for (size_t i = 0; i < HashMaxSize; i++){if (ht->data[i].key == key && ht->data[i].stat == Valid){*cur = i;return 1;}}return 0;
}
void HashRemove(HashTable* ht, KeyType key)
{if (ht == NULL)  //非法输入return;//先用find函数查找key是否存在ValueType value = 0;size_t cur = 0;                      //得到要删除元素的下标int ret=HashTableFindCur(ht,key,&cur);//通过find函数得到key是否存在在哈希表中if (ret == 0)return;else{ht->data[cur].stat = Invalid;ht->size--;}
}int HashEmpty(HashTable* ht)
{if (ht == NULL)return 0;elsereturn ht->size > 0 ? 1 : 0;
}//求哈希表的大小
size_t HashSize(HashTable* ht)
{if (ht == NULL)return 0;return ht->size->data[i].stat=Empty;//HashElemht->data[i].key = 0;ht->data[i].value = 0;}ht->size = 0;
}void HashPrint(HashTable* ht,const char* msg)         //打印哈希表
{if (ht == NULL || ht->size == 0)return;printf("%s\n", msg);for (size_t i = 0; i < HashMaxSize; i++){if (ht->data[i].stat != Empty)printf("[%d]  key=%d  value=%d  stat=%d\n", i, ht->data[i].key,ht->data[i].value, ht->data[i].stat);}
}

代码测试结果如下:

该文件的测试代码:

#define __TestHead__ printf("\n-----------------------%s--------------------------\n",__FUNCTION__);void testHashTableInsert(HashTable* ht)
{__TestHead__;HashTableInit(ht);HashTableInsert(ht, 1, 1);HashTableInsert(ht, 2, 2);HashTableInsert(ht, 1001, 1001);HashTableInsert(ht, 1002, 1002);HashTableInsert(ht, 10001, 10001);HashTableInsert(ht, 1002, 1002);HashPrint(ht, "插入四个元素");
}void testHashTableFind(HashTable* ht)
{__TestHead__;HashTableInit(ht);HashTableInsert(ht, 1, 1);HashTableInsert(ht, 2, 2);HashTableInsert(ht, 1001, 1001);HashTableInsert(ht, 102, 102);HashTableInsert(ht, 10001, 10001);HashTableInsert(ht, 1002, 1002);ValueType value = 0;int ret = HashTableFind(ht, 1002, &value);printf("ret: expect=1    actual=%d\nvalue: expect=1002    actual=%d\n", ret,value);
}void testHashRemove(HashTable* ht)
{__TestHead__;HashTableInit(ht);HashTableInsert(ht, 1, 1);HashTableInsert(ht, 2, 2);HashTableInsert(ht, 1001, 1001);HashTableInsert(ht, 102, 102);HashTableInsert(ht, 10001, 10001);HashTableInsert(ht, 1002, 1002);HashPrint(ht, "插入完成后");HashRemove(ht, 2);HashPrint(ht, "删除一个元素");HashTableInsert(ht, 100001,100001);HashPrint(ht, "插入一个元素");}void test()
{HashTable ht;HashTableInit(&ht);testHashTableInsert(&ht);testHashTableFind(&ht);testHashRemove(&ht);
}
}

数据结构---哈希表的C语言实现相关推荐

  1. Python中常用的数据结构---哈希表(字典)

    Python中常用的数据结构-哈希表(字典) 常用的数据结构有数组.链表(一对一).栈和队列.哈希表.树(一对多).图(多对多)等结构. 在本目录下我们将讲解,通过python语言实现常用的数据结构. ...

  2. 数据结构——哈希表的详解与实现

    数据结构--哈希表(HashTable) 1.前言 ​ 当我们频繁的查找数据中的某个元素时,我们通常会选择数组来存放数据,因为数组的的内存是连续的,可以直接通过下标访问数据,但是它添加和删除数据比较麻 ...

  3. 数据结构哈希表的实现与设计

    数据结构哈希表查找姓名的课程设计 有没有大神能帮忙写一下这道题,课设的题目.用C++语言 问题描述:针对某公司中花名设计哈希表,并完成相应的建表和查表程序,基本要求: (1)假设花名为汉字拼音形式.名 ...

  4. java hashtable 数据结构_数据结构--哈希表(Java)

    数据结构--哈希表(Java) 介绍 哈希表 底层是 数组加链表 或者是 数组加二叉树 ,一个数组里面有多个链表,通过散列函数来提高效率 代码 package cn.guizimo.hashtab; ...

  5. LeetCode刷题——哈希表(python语言)

    LeetCode刷题--哈希表(python语言) 一.哈希表 1.1 哈希表的概念 哈希表,也叫散列表.其实可以很像python的字典,也就是键(key)值(Hash(key))对,最简单也最常用的 ...

  6. 算法笔记(三)特殊数据结构——哈希表、有序表、并查集、KMP、Manacher、单调栈、位图、大数据类题

    layout: post title: 算法笔记(三)特殊数据结构--哈希表.有序表.并查集.KMP.Manacher.单调栈.位图.大数据类题 description: 算法笔记(三)特殊数据结构- ...

  7. c语言散列表的构造和查找,简单的哈希表实现 C语言

    简单的哈希表实现 这是一个简单的哈希表的实现,用c语言做的. 原理 先说一下原理. 先是有一个bucket数组,也就是所谓的桶. 哈希表的特点就是数据与其在表中的位置存在相关性,也就是有关系的,通过数 ...

  8. 数据结构 — 哈希表

    目录 文章目录 目录 哈希表 哈希表 哈希表,又称为散列表,是根据键值对(Key/Value)进行访问的数据结构,它让 Value 经过哈希函数的转换映射到哈希表对应的位置上,查找效率非常高.哈希索引 ...

  9. 哈希表数据结构_Java数据结构哈希表如何避免冲突

    前言 一.哈希表是what? 这是百度上给出的回答: 简而言之,为什么要有这种数据结构呢? 因为我们想不经过任何比较,一次从表中得到想要搜索的元素.所以就构造出来了哈希表,通过某种函数(哈希函数)使元 ...

  10. openssl lhash 数据结构哈希表

    哈希表是一种数据结构,通过在记录的存储位置和它的关键字之间建立确定的对应关系,来快速查询表中的数据: openssl lhash.h 为我们提供了哈希表OPENSSL_LHASH 的相关接口,我们可以 ...

最新文章

  1. LeetCode 143. 重排链表(Reorder List)
  2. [性能] SAP销售订单取数逻辑优化---索引表
  3. ad中电源插座怎么封装_您可以在房屋中安装的各种电源插座
  4. spring—SpringMVC的请求和响应
  5. 韩国首尔公交车站将被指定为禁烟场所
  6. python操作mysql数据库的常用方法使用详解
  7. PHP扩展迁移为PHP7扩展兼容性问题记录
  8. IJCAI2021论文:MEDA:一种为小样本文本分类设计的结合数据增强的元学习框架
  9. 网管必读-常用网络命令
  10. html5 单页视差模板,HTML5+CSS3的单页视差模板
  11. 资源 | 一网打尽成语歇后语,GitHub新华字典数据库
  12. linux 与mac使用类似telnet 工具
  13. Android源码 目录
  14. 思科 mds 虚拟服务器,Cisco MDS系列交换机VSAN功能简介
  15. 分享WEB快速开发工具
  16. Java图像识别技术:Test4J
  17. 淘宝API接口:item_search - 按关键字搜索淘宝商品
  18. 关闭WIN7休眠功能
  19. php执行fastlane,fastlane使用说明
  20. MyBatis和Hibernate的区别

热门文章

  1. 10款精美的web前端源码的特效
  2. php swool 聊天室,swoole简单的聊天室demo(修正版)
  3. HTML5 WebSockets 基础使用教程
  4. pdfobject.js和pdf.js的详解
  5. SQL教程——常见的约束类型
  6. c++ vector随机排序
  7. ext2文件系统初步
  8. ResNet网络结构
  9. linux操作系统和ucos操作系统,嵌入式操作系统ucos与linux比较
  10. 迁移到MySQL的语法转换工具初步设计