【算法导论】简单哈希表的除法实现
哈希表,又名散列表。hashtable。
。。
云云,看似非常高大上,事实上只是是直接寻址的延伸而已。直接寻址为何物,看一个数组:a[10],那么取当中一个元素a[1],这就是直接寻址,直接去这个a+1的地址上,就找到了这个数值,时间复杂度为O(1)。
而哈希表的目的就是要让查找的时间复杂度尽量往O(1)上靠。
一、哈希表的最简单形式
假如有10000个数,比方0~9999,是可能出现的数字的集合,我们如今要将一段时间内,出现的数字,所有保存起来。假设出现的数字都不反复的情况下。我们能够使用一个长度为10000的数组a[10000]来保存。假设数字987出现了。那么我们直接将其保存在a[987]元素上,以此类推。当我们要查询某个数是否出现的时候,比方1002这个数,是否出现。我们能够直接去找a[1002]。看看值是否是1002,为了保险起见。也能够在初始化的时候将数组做-1初始化。这样。查找的时间复杂度就是O(1)。
二、改进的哈希表
上面的做法感觉是非常快。可是却有问题。太浪费空间了。还是上面的样例,假如可能出现的集合为0~9999这一万个数。可是我们能够预知,实际会出现的数字仅仅有最多10个。假设依照上面的做法。就须要为查找这10个数。而花费10000个空间?是不是太奢侈了点?
其次,上面的做法,基础在于全部出现的数组。都不反复,假设我们人品太好,总共出现两个888怎么办?我们又想知道。888出现了几次?显然这时候。一个a[888]空间是不够的。
为了解决上面两个问题,分两步来做:
1、压缩存储空间。
怎样压缩?这里要用到通常所说的哈希函数。哈希函数的作用,就是将大的集合数据,印射到一个相对较小的,我们能够接受的集合范围内。使得速度和内存空间达到一个平衡。比方这里。0~9999一万个可能出现的数字集合。而最多实际仅仅会出现10个。我们就能够使用a%b(取余)操作来处理,比方这里,我们能够使用a%10。来让全部出现的数据的范围由0~9999,变成0~9这十个数,然后就能够使用一个a[10]的数组,去搞定直接寻址。
2、使用链表解决反复出现数据的问题
像上面说的,出现两个888怎么搞?那么我们在a[888]这个位置上。不放元素。我们将a数组作为一个链表数组,a[888]放链表的位置,这样,出现两个888,每次都从链表的头部插入,这样就能放的下了。
假设查找的时候,时间复杂度就不能是单单的O(1)了。我们考虑最坏的情况,比方n个元素的集合,数组的长度为m。当然(n>m)。这时候,除却哈希函数的取余操作的O(1),还要加上(n/m)的链表长度的查找,这是在全部位置链表的长度都同样的情况。假设链表的情况非常极端。。。这就不好了。。
所以依据上面的分析,不难发现,这个哈希函数(散列函数)非常关键,最好是能让数据,平均的分布到各个位置的链表上,而不要集中到一个或某几个。由于这样会造成某一个链表的长度非常长,那么查询起来,时间复杂度就不理想了。
关于散列函数(哈希函数)的取法,有非常多种。这里就不再讨论,以下给出除法(取余法)的散列函数,实现简单的哈希表代码:
#include <iostream>
using namespace std;/*** @作者:Alex/苦咖啡* @时间:2015.03.18* @博客:http://blog.csdn.net/cyp331203*/struct node {int key;node* next;node* pre;
};struct List {node* head;List() :head(NULL) {}~List() {node* tmp = NULL;while (head != NULL) {tmp = head->next;delete head;head = tmp;}}void print() {node* tmp = head;while (tmp != NULL) {cout << tmp->key << ' ';tmp = tmp->next;}cout << endl;}void insertHead(int key) {if (head != NULL) {node* n = new node();n->key = key;n->next = head;head->pre = n;head = n;} else {head = new node();head->key = 5;}}node* search(int key) {node* pre = NULL;node* curr = head;while (curr != NULL && curr->key != key) {pre = curr;curr = curr->next;}if (curr == NULL) {return NULL;} else if (pre == NULL) {return this->head;} else {return pre->next;}}void deleteNode(node* n) {if (n != NULL) {n->pre->next = n->next;n->next->pre = n->pre;delete n;}}
};//void deleteList(List* l) {
// if (l != NULL && (l->head) != NULL) {
// node* pre = NULL;
// node* curr = l->head;
// while (curr != NULL) {
// pre = curr;
// delete curr;
// curr = pre->next;
// }
// }
//}struct HashTable {List* arr;HashTable() {arr = new List[100];}//析构~HashTable() {for (int i = 0; i < 100; i++) {if (arr + i != NULL) {
// deleteList(arr + i);delete (arr+i);}}}void CHAINED_HASH_INSERT(int key) {(arr + (key % 100))->insertHead(key);}node* CHAINED_HASH_SEARCH(int key) {return (arr + (key % 100))->search(key);}void CHAINED_HASH_DELETE(node* n) {if (n != NULL) {(arr + (n->key % 100))->deleteNode(n);}}};int main() {HashTable* ht = new HashTable();ht->CHAINED_HASH_INSERT(5);node* n = ht->CHAINED_HASH_SEARCH(5);// cout << n->key << endl;ht->CHAINED_HASH_INSERT(205);// n.print();ht->arr[5].print();return 0;
}
【算法导论】简单哈希表的除法实现相关推荐
- 【MIT算法导论】哈希表、全域哈希
哈希表及哈希算法 一. 直接寻址 1. 应用条件 当关键字的全域比较小时(也即:候选的关键字的数目较少),直接寻址法才是行之有效的. p.s. 借用下图,即U(全域)很小,那么需要存储的部分K(实际需 ...
- 数据结构与算法五:哈希表-哈希函数设计原则-哈希冲突解决方案
一.哈希表的定义: 二.哈希表举例: 哈希函数就是映射关系 三.哈希表应用举例: Leetcode上第387题: 思路:通过s.charAt(i)-'a'将字符串中的字符映射成hash表,出现一次,在 ...
- c语言链地址法构造哈希表,链地址处理法构造简单哈希表
链地址法:将所有关键字为同义词的记录保存在一个线性链表中(拉链法) 设某哈希函数产生的哈希地址在区间[0,12]上,则创建指针数组add[12],其中每个元素都是一个单项链表的头结点(有值). 由于仅 ...
- 【数据结构与算法篇】 哈希表原理、底层实现剖析
一个在校大二学生,在CSDN记录自我成长!!!最近在自学数据结构和算法时,学到了哈希表,有很多地方都不明白.如何使用哈希表?原理是什么?如何工作的?我们如何设计哈希表?等等,所以就在网络上查了相关博客 ...
- python【数据结构与算法】深入浅出哈希表
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列 ...
- 【算法导论33】跳跃表(Skip list)原理与java实现
Skip list是一个用于有序元素序列快速搜索的数据结构,由美国计算机科学家William Pugh发明于1989年.它的效率和红黑树以及 AVL 树不相上下,但实现起来比较容易.作者William ...
- 数据结构与算法笔记:哈希表——力扣389
原题: 给定两个字符串 s 和 t ,它们只包含小写字母.字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母.请找出在 t 中被添加的字母. 思路: 首先咱们抛开编程知识,就当它是咱们日常 ...
- C++(数据结构与算法):30---散列(哈希)表的介绍(散列函数、散列冲突、散列溢出)
一.散列(哈希)介绍 散列使用一个散列函数(也称为哈希函数)把字典的数对映射到一个散列表(也称为哈希表)的具体位置 散列的存储与查找: 查找:如果数对p的关键字是k,散列函数为f,那么在理想的情况下, ...
- 数据结构与算法——深入理解哈希表
文章目录 哈希表 优点与缺点 哈希化 冲突 基于线性探测的开放地址法 插入 查找 删除 性能问题 基于二次探测的开放地址法 基于再哈希法的开放地址法 链地址法 哈希表实现方法的选择 参考 哈希表 哈希 ...
最新文章
- php.ini Xdebug配置
- 网络编程1之计算机网络及参考模型、域名、服务器
- matlab 文件之间相互调用实例
- matlab实现层次分析法
- WORD网址单词自动换行留下大量空白区?
- linux mc服务器 mod_我的世界:mc有哪些不为人知的“内幕”?Hypixel停服事件的真相...
- 五分钟快速过完Verilog HDL基本概念(2)
- XP和win7的软件崩溃提示
- 函数对象function object 以及boost::bind的一点了解
- 安卓 控件靠右对齐_LinearLayout中组件右对齐
- 9GAG 中用到的 Shimmer 是什么?
- 互联网老辛带你了解云架构集群
- wps2019数据分析加载项_wps单因素分析数据 wps2019单因素方差分析
- 小酷智慧地图3D导览v1.0.82 打卡定位 地图打卡
- 树莓派设置自动连接无线网络
- 15.大数据---Mapreduce案例之---统计手机号耗费的总上行流量、下行流量、总流量
- 2019年我的技术自我救赎之路
- Nginx listen指令处理连接请求
- 混迹在腾讯微博的知名站长名单
- Fully Attentional Network for Semantic Segmentation
热门文章
- python3.x执行post请求时报错“POST data should be bytes or an iterable of bytes...”的解决方法...
- UI Framework-1: Ash Color Chooser
- SQL Server job突然不工作了
- spring boot 使用application.properties 进行外部配置
- J2EE 第二阶段项目(八)
- Android 模块化编程之引用本地的aar
- Solr安装及集成javaWeb
- 从绝望中寻找希望,人生必将辉煌
- 大数据可视化应用在哪些方面
- html5 Canvas 绘制基本图形 从直线图形到使用路径 - 直线、矩形、路径、多边形、复杂组合图形