哈希表之一初步原理了解

考虑如下场景:小明住在芳华小区,芳华小区中有很多幢房子,每个房子有十几二十层,每层有4个住户;

现在小红要去找小明玩耍,现在假设小红只知道小明住在芳华小区,但是不知道住在哪一幢,哪一层和哪一户,】、

那么小红要怎么才能找到小明呢?

那么毫无疑问,小红只有在芳华小区中一家一家地敲门问小明住在哪儿?

(此时不允许小红叫一个收破烂的拿着喇叭在楼下喊:"小明,额想你!")

这个时候,如果小红有芳华小区的所有住户的明细的话,就可以从明细上找到小明住的地方,然后根据这个地方在芳华小区中找到小明,

而不需要敲遍小区所有住户的门。

同样,现在内存中有一数组,我们需要判断在数据中是否存在某个元素的时候,如果没有其他额外的信息,唯一的办法就是遍历这个数组,

因为这个时候我们只有这个这个数组的下标可用。

那么联想到上面小红找小明的例子,其实有类似之处,数组中的元素和下标现在是没什么联系的;不能根据元素值来定位到元素如果存在

在数组中的时候应该在哪个位置,要是我们想小红一样,也有一个"住户的明细"的话,任意给一个元素,我们根据元素的值找到其对应

在数组中的下标的值,然后直接根据这个下标的值直接去访问数组,便可以知道这个元素是否存在了。

现在出现了两个需要注意的东西:

(1)"住户的明细"

(2)找到

"住户的明细"其实从数学上来说可以看做一个对应关系,一个映射,也就是现在要讨论的哈希表。这个表记录了元素和其在数组中下标的

对应关系,待会我们就会对某个数组做一张这样的表;

"找到",其实就是根据元素的值计算出其在数组中的下标(位置)。

哈希表中有个两需要解决的问题:

(1)找到一个尽可能将元素分散的放到数组中去,不要让不同的元素都分到同一个位置去了

(2)万一确实通过hash函数计算之后分到了同一个地方,我们得有相应的措施来处理这种情况

现在有如下几个数:1,2,4,5,7,8,10,11;现在为了方便查找,我们可以这样放

这里的数据放置的规则很简单:

1%7=1,放到数组的第1个位置;

2%7=2,放到数组的第2个位置;

......

11%7=4,放到数组的第4个位置;

这里元素的值对7求余就是所选择的hash函数。如果我们要找数组中是否存在某个数x,可以用x%7求得一个数,

这个数就是在这个数组中应该存在的位置,此时直接通过这个位置定位数组中的这个元素,就可以很快的判断这个数在数组中

存不存,而不需要一个一个遍历数组。

图中可以看到1和8对7求余的余数是相同的,导致了它们被放到了同一个位置上,这就是属于冲突的情况。

哈希表中解决冲突的情况有两种方法:

一种方法就是如果冲突了的话就纵向的添加冲突的元素,如图中那样,先用hash算出元素应该出现的坑,然后在通过链表进行普通查找;

另一种方法就是继续横向的添加,具体怎么添加还没摸清楚~,反正hash基本原理就是这样的。

哈希表的如果建的好的话,查找元素的时间复杂度是O(n)哦,典型的时间换取空间。

有助于理解的图:

下面用C来写一个简单的hash表:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3
 4 #define LIST_LENGTH 100
 5
 6 typedef struct Item Item;
 7
 8 struct Item {     // 用链表来解决冲突
 9   int value;
10   int index;
11   Item * next;
12 };
13
14 typedef struct List {
15   Item *head;
16   Item *end;
17 } List;
18
19 void insertItem(List *list, Item *item) {
20
21   int key = (unsigned int)(item->value) % LIST_LENGTH; // 键的计算方法
22   // 判断list中是否存在元素
23   if(!list[key].head) {    // 这个槽之前没有元素
24     list[key].head = item;
25     list[key].end = item;
26   } else {                 // 已经有元素放到了这个位置
27     list[key].end->next = item;
28     list[key].end = item;
29   }
30 }
31
32 int searchValue(List *list, int value) {
33   // 计算出元素的键值
34   int key = (unsigned int)(value) % LIST_LENGTH;
35   Item *p = list[key].head;
36
37   while(p) {
38     if(value == p->value) {
39       return p->index+1; // 万一元素在数组中的索引就是在0处, 直接返回0的话和后面返回的0冲突了
40     }
41     p = p->next;
42   }
43   return 0;
44 }
45
46 int main(void) {
47
48   int array[] = {2, 3, 5, 7, 8, 9, -3, -8, 22, 13};
49   int num = 10;              // 数组元素的个数
50   int i = 0;
51   List list[LIST_LENGTH];    // 直接分配了这么一个结构体数组存放链表和key的对应关系
52   memset(list, 0, sizeof(List) * LIST_LENGTH);
53
54   Item item[10];             // 直接分配了一个结构体数组存放元素
55   memset(item, 0, sizeof(Item) * num);
56
57   // 遍历数组, 先将元素组装为一个结构体元素, 你可以看做面向对象编程中的对象
58   for(i=0; i<num; i++) {
59     item[i].value = array[i];
60     item[i].index = i;
61     insertItem(list, &item[i]);   // 把对象的引用放到hash表中
62   }
63
64   int test_arr[] = {-34, 2, 5, 42, -32, 12, 6, -8};
65   int num2 = 8;
66
67   printf("数组元素为:");
68   for(i=0; i<num; i++) {
69     printf("%d ", array[i]);
70   }
71   printf("\n");
72   printf("测试数组元素为:");
73   for(i=0; i<num2; i++) {
74     printf("%d ", test_arr[i]);
75   }
76   printf("\n\n");
77
78   for(i=0; i<num2; i++) {
79     int position = searchValue(list, test_arr[i]);
80     if(position) {
81       printf("数组array中存在元素%d, 下标为%d\n", test_arr[i], position-1);
82     } else {
83       printf("数组array中不存在元素%d\n", test_arr[i]);
84     }
85   }
86
87   return 0;
88 }

后续会关注的有:

1. 哈希表的变种,不过目前也就知道又一个一致性hash,这个实在memcached这个缓存框架中会用到;

2. java中其实已经实现了哈希表这种数据结构,用起来很方便,到时候可以跟一下源码;

3. java中的equals方法和hashcode方法的关系是什么样的,以及这两个方法应该如何去写才比较好;

时间: 2024-07-29 02:59:46

哈希表之一初步原理了解的相关文章

哈希表的原理与实现

[转自]:http://my.oschina.net/chape/blog/132533 目录[-] 哈希表的原理与实现 一致性 hash 算法 基本场景 hash 算法和单调性 consistent hashing 算法的原理 虚拟节点 小结 分布式哈希算法 哈希函数 哈希表 分布式哈希表 哈希表的工作原理与常用操作 基础操作 应用举例 哈希表的原理与实现 一列键值对数据,存储在一个table中,如何通过数据的关键字快速查找相应值呢?不要告诉我一个个拿出来比较key啊,呵呵. 大家都知道,在所

浅谈算法和数据结构: 十一 哈希表

在前面的系列文章中,依次介绍了基于无序列表的顺序查找,基于有序数组的二分查找,平衡查找树,以及红黑树,下图是他们在平均以及最差情况下的时间复杂度: 可以看到在时间复杂度上,红黑树在平均情况下插入,查找以及删除上都达到了lgN的时间复杂度. 那么有没有查找效率更高的数据结构呢,答案就是本文接下来要介绍了散列表,也叫哈希表(Hash Table) 什么是哈希表 哈希表就是一种以 键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值. 哈希的思路很简单

python数据结构与算法——哈希表

哈希表 学习笔记 参考翻译自:<复杂性思考> 及对应的online版本:http://greenteapress.com/complexity/html/thinkcomplexity004.html 使用哈希表可以进行非常快速的查找操作,查找时间为常数,同时不需要元素排列有序 python的内建数据类型:字典,就是用哈希表实现的 为了解释哈希表的工作原理,我们来尝试在不使用字典的情况下实现哈希表结构. 我们需要定义一个包含 键->值 映射 的数据结构,同时实现以下两种操作: add(k

哈希表工作原理 (并不特指Java中的HashTable)

1. 引言         哈希表(Hash Table)的应用近两年才在NOI中出现,作为一种高效的数据结构,它正在竞赛中发挥着越来越重要的作用.  哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间:而代价仅仅是消耗比较多的内存.然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的.另外,编码比较容易也是它的特点之一.         哈希表又叫做散列表,分为“开散列” 和“闭散列”.考虑到竞赛时多数人通常避免使用动态存储结构,本文中的“哈希表”仅

哈希表工作原理

1. 引言        哈希表(Hash Table)的应用近两年才在NOI中出现,作为一种高效的数据结构,它正在竞赛中发挥着越来越重要的作用. 哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间:而代价仅仅是消耗比较多的内存.然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的.另外,编码比较容易也是它的特点之一.        哈希表又叫做散列表,分为“开散列” 和“闭散列”.考虑到竞赛时多数人通常避免使用动态存储结构,本文中的“哈希表”仅指“闭

哈希表——swift字典的实现原理

我们先看看维基百科的介绍 散列表(Hash table,也叫哈希表),是根据关键字(Key value)而直接访问在内存存储位置的数据结构.也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度.这个映射函数称做散列函数,存放记录的数组称做散列表. 一个通俗的例子是,为了查找电话簿中某人的号码,可以创建一个按照人名首字母顺序排列的表(即建立人名到首字母的一个函数关系),在首字母为W的表中查找"王"姓的电话号码,显然比直接查找就要快得多.这

初步学习HashTable(哈希表或者散列链表)

初次接触哈希表,我谈谈自己对哈希表的一些理解,可能有误,还望指正. 对于哈希表,存放的数据是键值对<key,value>.是按照键值来索引的,键key可以是字符串.单个字符.整形数等,值value就是存放结点数据. 通俗的说,对于哈希表,使用数组来存放基本的结点,每个结点在挂上一串链表构成的结构,如下图所示: 数组存放的可以是不存储任何数据的头结点,我们的数据是存放在以为头结点开始的链表上的.图中的结点的中存放着键值对和指向下一个结点的指针. 索引原理:Java中是根据键进行二次哈希得到哈希值

查找--------哈希表的原理

这段时间 在 准备软件设计师考试    目的是想复习一下  自己以前没怎么学的知识    在这个过程中  有了很大的收获  对以前不太懂得东西  在复习的过程中  有了很大程度的提高 比如在复习 程序语言的时候    对编译程序的处理过程和文法分析 有了全新的了解 作为一个半路出家  没学过程序语言这门课的我来说   有一种醍醐灌顶的感觉   以前在看 javaweb技术内幕是 对里面提的javac 的编译原理  看的真的是 云里雾里   哈哈哈   想想 也是醉了  最基础的程序语言 都没有看

【Python算法】哈希存储、哈希表、散列表原理

哈希表的定义: 哈希存储的基本思想是以关键字Key为自变量,通过一定的函数关系(散列函数或哈希函数),计算出对应的函数值(哈希地址),以这个值作为数据元素的地址,并将数据元素存入到相应地址的存储单元中. 查找时再根据要查找的关键字采用同样的函数计算出哈希地址,然后直接到相应的存储单元中去取要找的数据元素即可. 哈希表的应用: 哈希表(hash table)是实现字典操作的一种有效的数据结构. 尽管最坏的情况下,散列表中查找一个元素的时间与链表中查找的时间相同,达到了O(n). 然而实际应用中,散