网络编程之 哈希表原理讲解 来自老司机的源码
鉴于博主很久没由跟新过数据结构的内容了,所以博主打算给大家讲解一下哈希表的操作
下面的内容来自于一位老司机 martin的源码,博主在这里借用一下,目的是突出哈希表的原理,明天博主就周末了,也能腾出时间来给上传自己的哈希表的应用。
这个是可以插入字符串的哈希表,一般的都是对数字的操作,所以这个的逼格是很高的!!!!(难点剖析放在最后)
#pragma once #define DEFAULT_SIZE 16 /*哈希表元素定义*/
typedef struct _ListNode{struct _ListNode *next;int key;void *data;
}ListNode;typedef ListNode *List;
typedef ListNode *Element;/*哈希表结构定义*/
typedef struct _HashTable{int TableSize;List *Thelists;
}HashTable;/*哈希函数*/
int Hash(void *key, int TableSize);/*初始化哈希表*/
HashTable *InitHash(int TableSize);/*哈希表插入*/
void Insert(HashTable *HashTable, int key, void *value);/*哈希表查找*/
Element Find(HashTable *HashTable, const int key);/*哈希表销毁*/
void Destory(HashTable *HashTable);/*哈希表元素中提取数据*/
void *Retrieve(Element e);
hash_table.cpp#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include"hash_table.h"/*根据 key 计算索引,定位 Hash 桶的位置*/
int Hash(int key, int TableSize) { return (key%TableSize); //返回索引值}/*初始化哈希表*/
HashTable *InitHash(int TableSize){ //传入 哈希桶的个数,在函数内部给哈希表分配空间,再将初始化好了的哈希表传出去。int i = 0;HashTable *hTable = NULL;if (TableSize <= 0) { //如果用户恶意输入数字,那么我们就可以把哈希表的个数定为我们最初的 16个TableSize = DEFAULT_SIZE;}hTable = (HashTable *)malloc(sizeof(HashTable));if (NULL == hTable){ //如果地址分配失败printf("HashTable malloc error.\n");return NULL;}hTable->TableSize = TableSize;//为 Hash 桶分配内存空间,其为一个指针数组 hTable->Thelists = (List *)malloc(sizeof(List)*TableSize); //hTable->Thelists其实就是可以来指向哈希表里面的与元素了if (NULL == hTable->Thelists){ //分配空间失败,打印错误,然后返回printf("HashTable malloc error\n");free(hTable);return NULL;}//为 Hash 桶对应的指针数组初始化链表节点 for (i = 0; i < TableSize; i++){hTable->Thelists[i] = (ListNode *)malloc(sizeof(ListNode));//也就是给头节点分配空间地,这样后续就能完成插入了if (NULL == hTable->Thelists[i]){ //如果分配失败,那我们就释放之前分配了的空间地址,以免造成内存泄漏printf("HashTable malloc error\n");free(hTable->Thelists);free(hTable);return NULL;} else{memset(hTable->Thelists[i], 0, sizeof(ListNode)); //将好小标指向的元素全部清空。主要是防止意外情况。}}return hTable;
}Element Find(HashTable *HashTable, int key)
{int i = 0; //目的接受调用HashTable函数返回的索引值List L = NULL; //定位到(第几个哈希桶)指针数组里面的第几个头结点。Element e = NULL; //指向定位到的(第几个哈希桶)的第一个节点(第一个节点是不包括头结点的哦)。i = Hash(key, HashTable->TableSize);L = HashTable->Thelists[i];e = L->next;while (e != NULL && e->key != key) //目的是为了遍历对应的哈希桶是否存在相同的key值。e = e->next;return e; //返回两种情况,找到了就返回e对应的结构体,失败了就返回NULL
}/*哈希表插入元素,元素为键值对*/
void Insert(HashTable *HashTable, int key, void *value)
{Element e = NULL, tmp = NULL; //e用来接收查找后的e,tmp是用来插入的List L = NULL; //定位到第几个哈希桶,和我们的find中的L的作用是一样的。e = Find(HashTable, key); //现在就找到了对应的第几个哈希桶的第一个节点了。或者是NULLif (NULL == e){ //如果接收到的e为空的话我们就可以进行插入元素的操作了tmp = (Element)malloc(sizeof(ListNode)); // 因为我们接收到的是NULL所以非配空间为了插入if (NULL == tmp){printf("malloc error\n");return;}L = HashTable->Thelists[Hash(key, HashTable->TableSize)]; //\定位到第几个哈希桶//经典的头插法tmp->data = value;tmp->key = key;tmp->next = L->next;L->next = tmp;} elseprintf("the key already exist\n");
}/*哈希表删除元素,元素为键值对*/
void Delete(HashTable *HashTable, int key){ //key是你要删除的第几个位置的键值对Element e = NULL, last = NULL; List L = NULL;int i = Hash(key, HashTable->TableSize); //找到对应的i是第几个L = HashTable->Thelists[i]; //找到对应的第几个哈希桶last = L; //让last 等于 对应的第几个哈希桶的头结点e = L->next; //e指向对应的第几个哈希桶的第一个节点。while (e != NULL && e->key != key) { //遍历对应的哈希桶的全部链表,结束条件是 找到了要删除的key或者是遍历完成last = e; //last为e也就是以后要删除的结点的上一个结点e = e->next; //指向后面的结点}if (e) {//如果键值对存在,那么就可以删除了。last->next = e->next; delete(e);}
}/*哈希表元素中提取数据*/
void *Retrieve(Element e) //在元素e存在的情况下降找到的 e->data 返回出去
{return e ? e->data : NULL; //三目运算符
}/*销毁哈希表*/
void Destory(HashTable *HashTable)
{int i = 0;List L = NULL;Element cur = NULL, next = NULL;for (i = 0; i < HashTable->TableSize; i++) //将所有的哈希桶都遍历完成{L = HashTable->Thelists[i];cur = L->next;while (cur != NULL) //将对应的链表全部销毁,跳出之后只有头结点没有销毁了{next = cur->next;free(cur);cur = next;}free(L); //释放那一个头结点} //完成之后所有的哈希桶全部销毁free(HashTable->Thelists); //释放那个二级指针free(HashTable); //释放哈希表。
}int main(void)
{char *elems[] = { "翠花","小芳","苍老师" };int i = 0;HashTable *HashTable;HashTable = InitHash(31);Insert(HashTable, 1, elems[0]);Insert(HashTable, 2, elems[1]);Insert(HashTable, 3, elems[2]);Delete(HashTable, 1);for (i = 0; i < 4; i++) {Element e = Find(HashTable, i);if (e) {printf("%s\n", (const char *)Retrieve(e));} else {printf("Not found [key:%d]\n", i);}}system("pause");return 0;
}
希望大家能好好的观看我的注释,相信一定能给你收获的。
如果觉得代码太长的,博主在这里给大家将模块分解了。大家也可以观看分解之后的代码,这样
压力会小一点。
头文件就不用说了相信大家都能看明白,就只是声明和定义结构体的类型而已。
哈希函数
/*根据 key 计算索引,定位 Hash 桶的位置*/
int Hash(int key, int TableSize) { return (key%TableSize); //返回索引值}
哈希表的初始化
/*初始化哈希表*/
HashTable *InitHash(int TableSize){ //传入 哈希桶的个数,在函数内部给哈希表分配空间,再将初始化好了的哈希表传出去。int i = 0;HashTable *hTable = NULL;if (TableSize <= 0) { //如果用户恶意输入数字,那么我们就可以把哈希表的个数定为我们最初的 16个TableSize = DEFAULT_SIZE;}hTable = (HashTable *)malloc(sizeof(HashTable));if (NULL == hTable){ //如果地址分配失败printf("HashTable malloc error.\n");return NULL;}hTable->TableSize = TableSize;//为 Hash 桶分配内存空间,其为一个指针数组 hTable->Thelists = (List *)malloc(sizeof(List)*TableSize); //hTable->Thelists其实就是可以来指向哈希表里面的与元素了if (NULL == hTable->Thelists){ //分配空间失败,打印错误,然后返回printf("HashTable malloc error\n");free(hTable);return NULL;}//为 Hash 桶对应的指针数组初始化链表节点 for (i = 0; i < TableSize; i++){hTable->Thelists[i] = (ListNode *)malloc(sizeof(ListNode));//也就是给头节点分配空间地,这样后续就能完成插入了if (NULL == hTable->Thelists[i]){ //如果分配失败,那我们就释放之前分配了的空间地址,以免造成内存泄漏printf("HashTable malloc error\n");free(hTable->Thelists);free(hTable);return NULL;} else{memset(hTable->Thelists[i], 0, sizeof(ListNode)); //将好小标指向的元素全部清空。主要是防止意外情况。}}return hTable;
}
哈希表中插入元素:
/*哈希表插入元素,元素为键值对*/
void Insert(HashTable *HashTable, int key, void *value)
{Element e = NULL, tmp = NULL; //e用来接收查找后的e,tmp是用来插入的List L = NULL; //定位到第几个哈希桶,和我们的find中的L的作用是一样的。e = Find(HashTable, key); //现在就找到了对应的第几个哈希桶的第一个节点了。或者是NULLif (NULL == e){ //如果接收到的e为空的话我们就可以进行插入元素的操作了tmp = (Element)malloc(sizeof(ListNode)); // 因为我们接收到的是NULL所以非配空间为了插入if (NULL == tmp){printf("malloc error\n");return;}L = HashTable->Thelists[Hash(key, HashTable->TableSize)]; //\定位到第几个哈希桶//经典的头插法tmp->data = value;tmp->key = key;tmp->next = L->next;L->next = tmp;} elseprintf("the key already exist\n");
}
哈希表中查找元素:
Element Find(HashTable *HashTable, int key)
{int i = 0; //目的接受调用HashTable函数返回的索引值List L = NULL; //定位到(第几个哈希桶)指针数组里面的第几个头结点。Element e = NULL; //指向定位到的(第几个哈希桶)的第一个节点(第一个节点是不包括头结点的哦)。i = Hash(key, HashTable->TableSize);L = HashTable->Thelists[i];e = L->next;while (e != NULL && e->key != key) //目的是为了遍历对应的哈希桶是否存在相同的key值。e = e->next;return e; //返回两种情况,找到了就返回e对应的结构体,失败了就返回NULL
}
哈希表中删除元素:
/*哈希表删除元素,元素为键值对*/
void Delete(HashTable *HashTable, int key){ //key是你要删除的第几个位置的键值对Element e = NULL, last = NULL; List L = NULL;int i = Hash(key, HashTable->TableSize); //找到对应的i是第几个L = HashTable->Thelists[i]; //找到对应的第几个哈希桶last = L; //让last 等于 对应的第几个哈希桶的头结点e = L->next; //e指向对应的第几个哈希桶的第一个节点。while (e != NULL && e->key != key) { //遍历对应的哈希桶的全部链表,结束条件是 找到了要删除的key或者是遍历完成last = e; //last为e也就是以后要删除的结点的上一个结点e = e->next; //指向后面的结点}if (e) {//如果键值对存在,那么就可以删除了。last->next = e->next; delete(e);}
}
哈希表中提取元素以及销毁哈希表:
/*哈希表元素中提取数据*/
void *Retrieve(Element e) //在元素e存在的情况下降找到的 e->data 返回出去
{return e ? e->data : NULL; //三目运算符
}/*销毁哈希表*/
void Destory(HashTable *HashTable)
{int i = 0;List L = NULL;Element cur = NULL, next = NULL;for (i = 0; i < HashTable->TableSize; i++) //将所有的哈希桶都遍历完成{L = HashTable->Thelists[i];cur = L->next;while (cur != NULL) //将对应的链表全部销毁,跳出之后只有头结点没有销毁了{next = cur->next;free(cur);cur = next;}free(L); //释放那一个头结点} //完成之后所有的哈希桶全部销毁free(HashTable->Thelists); //释放那个二级指针free(HashTable); //释放哈希表。
}
博主认为比较难的就是:
指针数组里面存放的是每个哈希桶的头结点,通过求余来锁定要查找或删除的值在哪一个哈希桶里(认为就是这个最难理解,理解了之后其实哈希就不难了)。其他的就是链表的操作了。
明天上传自己敲得代码
网络编程之 哈希表原理讲解 来自老司机的源码相关推荐
- Java:实现具有开放寻址冲突解析方法(如linear)的哈希表基类算法(附完整源码)
Java:实现具有开放寻址冲突解析方法的哈希表基类算法 package com.williamfiset.algorithms.datastructures.hashtable;import java ...
- 基于linux epoll网络编程细节处理丨epoll原理剖析
epoll原理剖析以及三握四挥的处理 1. epoll原理详解 2. 连接的创建与断开 3. epoll如何连接细节问题 视频讲解如下,点击观看: 基于linux epoll网络编程细节处理丨epol ...
- java毕业设计——基于Java+Java ME的无线网络移动端的俄罗斯方块游戏设计与实现(毕业论文+程序源码)——俄罗斯方块游戏
基于Java+Java ME的无线网络移动端的俄罗斯方块游戏设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于Java+Java ME的无线网络移动端的俄罗斯方块游戏设计与实现,文章末尾附有 ...
- 服务器端编程心得(七)——开源一款即时通讯软件的源码
服务器端编程心得(七)--开源一款即时通讯软件的源码 2017年04月06日 22:57:01 analogous_love 阅读数:30222更多 所属专栏: 高性能服务器编程实现细节详解 版权声明 ...
- java毕业设计——基于java+Jsoup+HttpClient的网络爬虫技术的网络新闻分析系统设计与实现(毕业论文+程序源码)——网络新闻分析系统
基于java+Jsoup+HttpClient的网络爬虫技术的网络新闻分析系统设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+Jsoup+HttpClient的网络爬虫技术的网络 ...
- asp毕业设计——基于asp+sqlserver的网络教学评教管理信息系统设计与实现(毕业论文+程序源码)——教学评教管理信息系统
基于asp+sqlserver的网络教学评教管理信息系统设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于asp+sqlserver的网络教学评教管理信息系统设计与实现,文章末尾附有本毕业设 ...
- 云原生服务网格Istio:原理、实践、架构与源码解析
华为云原生团队600多页的Istio实战精华总结,云原生服务网格Istio:原理.实践.架构与源码解析的电子书. 图书介绍 <云原生服务网格Istio:原理.实践.架构与源码解析>分为原理 ...
- python3网络爬虫--爬取b站用户投稿视频信息(附源码)
文章目录 一.准备工作 1.工具 二.思路 1.整体思路 2.爬虫思路 三.分析网页 1.分析数据加载方式 2.分词接口url 3.分析用户名(mid) 四.撰写爬虫 五.得到数据 六.总结 上次写了 ...
- 【通知】▁▂▃ Himi 最新著作《iOS游戏编程之从零开始—Cocos2d-x与cocos2d引擎游戏开发》★书籍源码+第4/5/6样章★-免费下载★ ▃▂▁
2013年新年,Himi的第二本著作:<iOS游戏编程之从零开始-Cocos2d-x与cocos2d引擎游戏开发>一书正式发售: (大家可以到新华书店.淘宝.拍拍.当当.亚马逊等进行购买) ...
最新文章
- 从零开始写个编译器吧 - 单词化简述(Tokenization)
- python的用途实例-python中pass语句意义与作用(实例分析)
- Leetcode 45. 跳跃游戏 II (每日一题 20210922)
- 如何使用Prometheus采集SAP ABAP Netweaver的应用日志数据
- insert ... on duplicate key update产生death lock死锁原理
- Don't Laugh!I'm An English Book笔记(五)——面部词语大总结加补充
- asp.net调用js方法小结
- 2021高通人工智能创新大赛垃圾分类赛题第五次研讨会
- HDU 6241 Color a Tree
- mysql查找配置文件的顺序
- python-万年历
- VSCode配置vue用户代码片段Snippets
- 模拟CMOS集成电路学习笔记——MOS器件物理基础
- lisp 焊缝标注_钢结构深化设计实施方案.doc
- 按键精灵打怪学习-多线程后台坐标识别
- 电影推荐算法及python实现
- x86 单线并发多拨_OpenWrt ADSL单线多拨,负载均衡(仅供参考)
- flutter 刷脸_传说哥教你如何假装架构师
- Elasticsearch——Bboss
- html5程序员面试官如何提问,前端程序员第二轮面试的10个问题
热门文章
- 剑指offer之链表中倒数第K个节点
- 中国吉他效果市场趋势报告、技术动态创新及市场预测
- php5+init,PHP mysqli_stmt_init() 函数
- 标准IO库fgets和fputs对一个文本文件的读写操作
- Easystructure教程_C语言源代码自动生成流程图
- 历史上的今天:雅虎正式成立;PC 设计先驱诞生;Excite@Home 破产
- 放弃使用 15 年的 macOS,我决定换成 Linux!
- 程序员拒带电脑回家被开除获赔 19.4 万;库克称,很多功能来自中国消费者反馈;谷歌开源1.6万亿参数语言模型 | 极客头条...
- 什么是 “内存管理机制”?
- 挑战王者荣耀“绝悟” AI,会进化的职业选手太恐怖了!