哈希表拉链法

合集下载

Java散列表以拉链法解决冲突问题(以电话簿为例)

Java散列表以拉链法解决冲突问题(以电话簿为例)

Java散列表以拉链法解决冲突问题(以电话簿为例)原理哈希表的结构哈希表⼜被称为数组链表。

当插⼊删除操作和取值操作都较频繁时,我们可以采⽤哈希表来作为集合的数据结构。

定义:哈希表(Hash table,也叫散列表),是根据关键码值(Key value)⽽直接进⾏访问的数据结构。

也就是说,它通过把关键码值映射到表中⼀个位置来访问记录,以加快查找的速度。

这个映射函数叫做散列函数,存放记录的数组叫做散列表。

⼤致结构如下但是本例题未采⽤Java⾃带的hash集合特点:1.第⼀列是⼀个数组。

因此我们在查找每⼀个链表头结点位置所耗费的时间复杂度都是常数1;2.每⼀⾏都是⼀个链表。

理论上,这个链表可以⽆限扩⼤。

实际上当然是不⾏的,我们可以设想两种极端情况。

⼀种是链表的长度远远⼤于头结点数组的长度,那么这时这个哈希表其实就相当于⼀个链表,它取值操作的时间复杂度还是接近n。

另⼀种情况就是链表的长度远远⼩于头结点数组的长度,那么这时这个哈希表其实就相当于⼀个数组,它插⼊和删除操作的时间复杂度还是接近n。

为了避免这两种极端情况的出现,我们引⼊了⼀个控制变量peakValue(当前哈希表的数据个数/数组长度)。

如果这个值超过了某⼀界限,我们就对当前的哈希表进⾏重构。

3.每⼀次存放和取出数据,都是先找到对应的⾏号(即头结点的位置),然后再去遍历该⾏链表中的各个数据。

哈希表的构建思路基本思路:⾸先我们需要开辟⼀个连续的数组来储存每个头结点,这个数组的⼤⼩是固定的。

每当我们从⽂件中读取出⼀个电话簿,⾸先要将其封装成⼀个节点。

然后根据number计算出相应的code,这个code会定位到唯⼀的⼀个链表头。

最后再把数据放到这个链表⾥⾯。

源代码import java.io.File;import java.io.FileNotFoundException;import java.util.Scanner;public class worktest {class Listnode {String name;String number;Object key;Listnode next;Listnode() {};Listnode(String name, String number) { = name;this.number = number;}Listnode(String name, String number, Listnode next) { = name;this.number = number;this.next = next;}}public void read_file(Listnode []node){File file = new File("d://电话号码.txt");if (!file.exists()){System.out.println("该⽂件不存在");System.exit(0);}try {Scanner scanner = new Scanner(file);while(scanner.hasNext()){String name = scanner.next();String number = scanner.next();Listnode listnode = new Listnode(name,number);listnode.next = null;int i = (number.charAt(10)-'0')%10;//除10取余// System.out.println(i);if(node[i] == null){//简单拉链法存数据node[i] = listnode;}else{listnode.next = node[i].next;node[i].next = listnode;}}} catch (FileNotFoundException e) {e.printStackTrace();}}public void show(Listnode []node){//输出电话簿Listnode listnode = new Listnode();for (int i = 0; i < 10; i++) {listnode = node[i];while (listnode!=null){System.out.println(+" "+listnode.number);listnode = listnode.next;}}}public void search1(Listnode []node, String name){//根据姓名查找 Listnode listnode = new Listnode();for (int i = 0; i < 10; i++) {listnode = node[i];while (listnode != null){if (.equals(name)){System.out.println(+" "+listnode.number); }listnode = listnode.next;}}}public void search2(Listnode []node, String number){//根据电话查找 Listnode listnode = new Listnode();for (int i = 0; i < 10; i++) {listnode = node[i];while (listnode != null){if (listnode.number.equals(number)){System.out.println(+" "+listnode.number); }listnode = listnode.next;}}}public static void main(String[] args) {Scanner in = new Scanner(System.in);worktest test = new worktest();Listnode []node = new Listnode[10];//key%10test.read_file(node);// test.show(node);while(true){System.out.println("请选择查找⽅式:1.按姓名查找,2.按电话查找,(输出其他退出)");int choice = in.nextInt();if (choice == 1){String name = in.next();long startTime = System.currentTimeMillis();//计算查找所消耗的时间test.search1(node,name);long endTime = System.currentTimeMillis();System.out.println("本次查找消耗时间为"+(endTime-startTime)+"微秒");}else if(choice == 2){String number = in.next();long startTime = System.currentTimeMillis();test.search2(node,number);long endTime = System.currentTimeMillis();System.out.println("本次查找消耗时间为"+(endTime-startTime)+"微秒");}elsebreak;}}}⽂件截图总结反思1.把⼀个String字符串转化为int型整数有两种意思。

拉链法装填因子

拉链法装填因子

拉链法装填因子拉链法是一种常用的哈希表处理冲突的方法,通过将哈希表的每个槽位设计为一个链表,解决了多个关键字映射到同一个槽位的问题。

本文将从拉链法的原理、实现方式、优缺点以及应用场景等几个方面进行详细介绍。

一、拉链法的原理拉链法是一种基于链表的哈希表处理冲突的方法,其核心思想是将哈希表的每个槽位设计为一个链表,将冲突的关键字通过链表进行串联。

当多个关键字经过哈希函数计算后映射到同一个槽位时,不再直接存储在槽位中,而是将其插入到对应链表的末尾。

二、拉链法的实现方式在实现拉链法时,首先需要设计一个合适的哈希函数,将关键字映射到对应的槽位。

然后,将每个槽位设计为一个链表,当有冲突发生时,将新的关键字插入到对应链表的末尾。

在进行查找操作时,先通过哈希函数找到关键字所在的槽位,再在对应的链表中进行查找。

三、拉链法的优缺点拉链法作为一种常用的哈希表处理冲突的方法,具有以下优点:1. 实现简单:只需要设计一个合适的哈希函数和链表即可。

2. 冲突处理高效:当冲突发生时,只需要在对应链表的末尾插入新的关键字,时间复杂度为O(1)。

3. 空间利用率高:可以动态的调整链表的长度,适应不同规模的数据。

然而,拉链法也存在一些缺点:1. 链表长度不均衡:由于关键字的分布是随机的,拉链法无法保证每个链表的长度均衡,可能导致某些链表过长,影响查找效率。

2. 内存占用较大:由于需要存储额外的链表信息,拉链法相对于开放寻址法会占用更多的内存空间。

3. 链表操作相对慢:由于链表的插入和删除操作相对慢,当链表长度过长时,可能会导致查找效率下降。

四、拉链法的应用场景拉链法在实际应用中有广泛的应用场景,尤其适用于以下情况:1. 数据量较大且分布较为均匀:拉链法对于分布较为均匀的数据能够保持较高的查找效率。

2. 冲突较少:当冲突发生较少时,拉链法能够有效地处理冲突,提高查找效率。

3. 对内存空间要求相对较高:相比于开放寻址法,拉链法可以动态调整链表的长度,因此对内存空间的利用率较高。

哈希表

哈希表
线性探测: 假定采用的H函数为: 线性探测: 假定采用的H函数为:H(key)= keyMOD11, keyMOD11, 关键字序列为:17、60、29、 关键字序列为:17、60、29、38 …
H( 17 ) = H( 60 ) = H( 29 ) = H( 38 ) = 6 5 7 5 Hashing 表 0 1 2 3 4 5 6 7 8 9 10
6 7 8
11 MOD 13=11 1 MOD 13=1 20 MOD 13=7 16 MOD 13=3 15 MOD 13=2 4 MOD 13=4
0
1
2
3
4
5
9
10
11
12
JulyMar Apr May Aug Oct Feb Sep Nov Dec
Jan June
9
( 2) 0 1 2 3 4 5 6 7 8 9 10 11 12 ∧
4
3. 链地址法 将所有哈希地址相同的记录都链接在同一链表中。 将所有哈希地址相同的记录都链接在同一链表中。链地 址肯定不会产生二次聚集。 址肯定不会产生二次聚集。
0 1 2 3 4 5 6 7 8 9

K1
∧ ∧ ∧
K2
K3

同义链,同一散列地∧
K5

公共溢出区 K2 K3 K5 K5 K6
∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧
7
∧ ∧ ∧
∧ ∧ ∧ ∧ ∧
课 堂 作

在地址空间为0 16的散列区中 在地址空间为0~16的散列区中,对以下关键字序列构造两 的散列区中, 个哈希表: 个哈希表: (Jan, Feb, Mar, Apr, May, June, July, Aug, Sep, Oct, Nov, Dec) Dec) 用线性探测开放定址法处理冲突( 根据首字母音序) ( 1 ) 用线性探测开放定址法处理冲突 ( 根据首字母音序 ) ; 用链地址法处理。 (2)用链地址法处理。 设哈希函数为H(x)=i 13,其中i 设哈希函数为H(x)=i MOD 13,其中i为关键字中第一个字 母在字母表中的序号。 母在字母表中的序号。

哈希表查找方法原理(一)

哈希表查找方法原理(一)

哈希表查找方法原理(一)哈希表查找方法简介什么是哈希表?•哈希表是一种用于存储键值对(key-value)数据的数据结构。

•哈希表的本质是一个数组,每个数组元素称为一个桶(bucket)或槽(slot)。

•每个桶可以存储一个或多个键值对,通过使用哈希函数将键映射为桶的索引。

哈希函数的作用•哈希函数将任意大小的数据映射到固定大小的值。

•这个值作为索引用于访问哈希表中特定桶的位置。

•好的哈希函数应该具备以下特点:–确定性:对于相同的输入,哈希函数应始终返回相同的哈希值。

–均匀性:哈希值应尽可能地分布均匀,避免冲突。

–高效性:哈希函数应具备高效计算的特点,以提高查找效率。

哈希表的查找方法1.哈希表查找的基本过程:–通过哈希函数计算出要查找元素的哈希值。

–使用哈希值作为索引,在哈希表中访问对应的桶。

–检查桶中的元素,进行比较以确定是否找到目标元素。

2.根据桶内元素的存储方式,哈希表的查找方法可分为两种基本类型:a.链地址法(拉链法)–桶中的每个位置都是一个链表,用于存储哈希值相同的键值对。

–查找时,首先计算哈希值,然后在相应的链表中顺序查找目标元素。

–链地址法适合处理冲突较多的情况,但链表过长会影响查找效率。

b.开放地址法(线性探测法、二次探测法等)–桶中的每个位置都可以存储一个键值对。

–当发生冲突时,通过一定的方法找到下一个可用的桶来存储冲突的元素。

–常用的探测方法包括线性探测、二次探测等。

–开放地址法适合处理冲突较少的情况,但可能会造成桶的利用率低下。

哈希表查找的时间复杂度•在理想情况下,哈希表的查找时间复杂度为O(1)。

•但在最坏情况下,查找时间复杂度可能会达到O(n),其中n为待查找元素的总数。

•好的哈希函数和适当的处理冲突方法可以尽量降低冲突的发生,提高查找效率。

总结•哈希表通过哈希函数将键映射为桶的索引,实现了高效的查找操作。

•哈希表的查找方法主要有链地址法和开放地址法,根据具体情况选择合适的方法。

HASH表

HASH表

hashing定义了一种将字符组成的字符串转换为固定长度(一般是更短长度)的数值或索引值的方法,称为散列法,也叫哈希法。

由于通过更短的哈希值比用原始值进行数据库搜索更快,这种方法一般用来在数据库中建立索引并进行搜索,同时还用在各种解密算法中。

设所有可能出现的关键字集合记为u(简称全集)。

实际发生(即实际存储)的关键字集合记为k(|k|比|u|小得多)。

|k|是集合k中元素的个数。

散列方法是使用函数hash将u映射到表t[0..m-1]的下标上(m=o(|u|))。

这样以u中关键字为自变量,以h为函数的运算结果就是相应结点的存储地址。

从而达到在o(1)时间内就可完成查找。

其中:①hash:u→{0,1,2,…,m-1} ,通常称h为散列函数(hash function)。

散列函数h 的作用是压缩待处理的下标范围,使待处理的|u|个值减少到m个值,从而降低空间开销。

②t为散列表(hash table)。

③hash(ki)(ki∈u)是关键字为ki结点存储地址(亦称散列值或散列地址)。

④将结点按其关键字的散列地址存储到散列表中的过程称为散列(hashing).比如:有一组数据包括用户名字、电话、住址等,为了快速的检索,我们可以利用名字作为关键码,hash规则就是把名字中每一个字的拼音的第一个字母拿出来,把该字母在26个字母中的顺序值取出来加在一块作为改记录的地址。

比如张三,就是z+s=26+19=45。

就是把张三存在地址为45处。

但是这样存在一个问题,比如假如有个用户名字叫做:周四,那么计算它的地址时也是z+s=45,这样它与张三就有相同的地址,这就是冲突,也叫作碰撞!冲突:两个不同的关键字,由于散列函数值相同,因而被映射到同一表位置上。

该现象称为冲突(collision)或碰撞。

发生冲突的两个关键字称为该散列函数的同义词(synonym)。

冲突基本上不可避免的,除非数据很少,我们只能采取措施尽量避免冲突,或者寻找解决冲突的办法。

哈希表——线性探測法、链地址法、查找成功、查找不成功的平均长度

哈希表——线性探測法、链地址法、查找成功、查找不成功的平均长度

哈希表——线性探測法、链地址法、查找成功、查找不成功的平均长度⼀、哈希表1、概念哈希表(Hash Table)也叫散列表,是依据关键码值(Key Value)⽽直接进⾏訪问的数据结构。

它通过把关键码值映射到哈希表中的⼀个位置来訪问记录,以加快查找的速度。

这个映射函数就做散列函数。

存放记录的数组叫做散列表。

2、散列存储的基本思路以数据中每⼀个元素的keywordK为⾃变量。

通过散列函数H(k)计算出函数值,以该函数值作为⼀块连续存储空间的的单元地址,将该元素存储到函数值相应的单元中。

3、哈希表查找的时间复杂度哈希表存储的是键值对,其查找的时间复杂度与元素数量多少⽆关。

哈希表在查找元素时是通过计算哈希码值来定位元素的位置从⽽直接訪问元素的,因此,哈希表查找的时间复杂度为O(1)。

⼆、经常使⽤的哈希函数1. 直接寻址法取keyword或者keyword的某个线性函数值作为哈希地址,即H(Key)=Key或者H(Key)=a*Key+b(a,b为整数),这样的散列函数也叫做⾃⾝函数.假设H(Key)的哈希地址上已经有值了,那么就往下⼀个位置找,知道找到H(Key)的位置没有值了就把元素放进去.2. 数字分析法分析⼀组数据,⽐⽅⼀组员⼯的出⽣年⽉,这时我们发现出⽣年⽉的前⼏位数字⼀般都同样,因此,出现冲突的概率就会⾮常⼤,可是我们发现年⽉⽇的后⼏位表⽰⽉份和详细⽇期的数字区别⾮常⼤,假设利⽤后⾯的⼏位数字来构造散列地址,则冲突的⼏率则会明显减少.因此数字分析法就是找出数字的规律,尽可能利⽤这些数据来构造冲突⼏率较低的散列地址.3. 平⽅取中法取keyword平⽅后的中间⼏位作为散列地址.⼀个数的平⽅值的中间⼏位和数的每⼀位都有关。

因此,有平⽅取中法得到的哈希地址同keyword的每⼀位都有关。

是的哈希地址具有较好的分散性。

该⽅法适⽤于keyword中的每⼀位取值都不够分散或者较分散的位数⼩于哈希地址所须要的位数的情况。

NOIP基础数据结构_哈希、并查集

NOIP基础数据结构_哈希、并查集

your site here
•解决冲突方法有多种,最常见的有“拉链 法”和“线性探测法”。下面主要讲解这 两种hash表的实现方法。
LOGO
哈希表(hash)
hash表的拉链法实现图示
•Key2与keyN冲突
your family site
your site here
Key1 Key2 Key3 . . . KeyN
hash表的拉链法实现pascal版
const
your family site
//注:本程序用数组模拟指针法编程
maxN = 1000000; maxM = 2000003; //大质数,通常 maxM > 2*maxN
type
Tnode =record x, c :longint; next :longint; end; //记录读入数据x和计数器c //用数组模拟指针,next是下一个元素下标
your family site
your site here
LOGO
哈希表(hash)
hash表的拉链法实现pascal版
begin
your family site
assign(input,'expa.in'); reset(input); assign(output,'expa.out'); rewrite(output); readln(n); for i:=1 to n do
your family site
your site here
•hash的思想是能直接找到需要的元素,因此必须 在元素的存储位置和它的关键字之间建立一确定 的对应关系f,使每个关键字和存储结构中一个( 几乎)唯一的存储位置相对应。

哈希冲突详解(拉链法,开放地址法)

哈希冲突详解(拉链法,开放地址法)

哈希冲突详解(拉链法,开放地址法)哈希冲突详解我喜欢⽤问答的形式来学习,这样可以明确许多不明朗的问题。

1. 什么是哈希冲突?⽐如我们要去买房⼦,本来已经看好的房⼦却被商家告知那间房⼦已经被其他客户买⾛了。

这就是⽣活中实实在在的冲突问题。

同样的当数据插⼊到哈希表时,不同key值产⽣的h(key)却是相等的,这个时候就产⽣了冲突。

这个时候就要解决这个问题。

怎么解决哈希冲突?⽅法1:拉链法⽅法2:开地址法何为拉链法?拉链法是解决哈希冲突的⼀种⾏之有效的⽅法,某些哈希地址可以被多个关键字值共享,这样可以针对每个哈希地址建⽴⼀个单链表。

在拉链(单链表)的哈希表中搜索⼀个记录是容易的,⾸先计算哈希地址,然后搜索该地址的单链表。

在插⼊时应保证表中不含有与该关键字值相同的记录,然后按在有序表中插⼊⼀个记录的⽅法进⾏。

针对关键字值相同的情况,现⾏的处理⽅法是更新该关键字值中的内容。

删除关键字值为k的记录,应先在该关键字值的哈希地址处的单链表中找到该记录,然后删除之。

什么是开地址法?⾸先该⽅法并不建⽴链表。

哈希表由M个元素组成,其地址从0到M-1。

我们通过从空表开始,逐个向表中插⼊新记录的⽅式建⽴散列表。

插⼊关键字值为key的新纪录的⽅法是:从h(key)开始,按照某种规定的次序探查插⼊新记录的空位置。

h(key)被称为基位置。

如果h(key)已经被占⽤,那么需要⽤⼀种解决冲突的策略来确定如何探查下⼀个空位置,所以这种⽅法⼜称为空缺编址法。

根据不同的解决冲突的策略,可以产⽣不同的需要被检查的位置序列,称为探查序列。

根据⽣成的探查序列的不同规则,可以有线性探查法、伪随机探查法、⼆次探查法和双散列法等开址⽅法。

线性探查法详解缺点:线性探查法在情况不好的时候导致许多记录在散列表中连成⼀⽚,从⽽使探查次数增加,影响搜索效率。

这种现象称为基本聚集。

线性探查法是⼀种简单的开地址⽅法,它使⽤下列循环探查序列:h(key),h(key)+1,...,M-1,0,...,h(key)-1从基位h(key)开始探查该位置是否被占⽤,即是否为空位置。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

#include<stdio.h>
#include<string.h>
#include<malloc.h>
#define MaxSize 100
#define NULLKEY -1
#define DELKEY -2
typedef int KeyType;
typedef char* InfoType;
typedef struct
{
KeyType key;//关键字
InfoType data;//其他数据
int count; //探查次数
}HashTable[MaxSize];
HashTable ha;
//拉链法查询
int SearchHT(HashTable ha,int p,KeyType k)
{
typedef struct node
{
int val;
node * next;
};
//拉链法
node *pt;
int i=0,adr;
adr=k%p;
// pt=(node*)malloc(sizeof(pt));
pt=(node*)ha[adr].key;
if(ha[adr].key!=NULLKEY)
{
while(pt!=NULL)
{
if(pt->val==k)
return ha[adr].key;//返回该元素所在链表的头指针pt=pt->next;
}
}
return -1;
}
//删除
void DeleteHT(HashTable ha,int p,int k,int n) {
int adr;
int adr2;
adr2=k%p;
typedef struct node
{
int val;
node * next;
};
//拉链法
node *pt,*r;
adr=SearchHT(ha,p,k);//查找关键字
pt=(node*)adr;
if(adr!=-1)
{
while(pt->val!=k)
{
r=pt;
pt=pt->next;
}
if(pt->val==k)
{
if((int)pt==adr)
{
if(pt->next==NULL)
ha[adr2].key=NULLKEY;
else
adr=(int)pt->next;
}
else
r->next=pt->next;
}
ha[adr2].count--;
}
else
printf("删除失败!");
}
//插入
void InsertHT(HashTable ha,int n,KeyType k,int p)
{/*
int i,adr;
adr=k%p;
if(ha[adr].key==NULLKEY||ha[adr].key==DELKEY) //直接插入哈希表{
ha[adr].key=k;
ha[adr].count=1;
}
else //采用线性探查的方法解决冲突
{
i=1;
do
{
adr=(adr+1)%p;
i++;
}
while( ha[adr].key!=NULLKEY && ha[adr].key!=DELKEY) ;
ha[adr].key=k;
ha[adr].count=i;
}
n++;*/
typedef struct node
{
int val;
node * next;
};
//拉链法
node *pt,*s;
int adr;
int i;
adr=k%p;
if(ha[adr].key==NULLKEY)
{
pt=(node*)malloc(sizeof(pt));
ha[adr].key=(int)pt;
pt->val=k;
pt->next=NULL;
ha[adr].count=1;
}
else
{
pt=(node*)ha[adr].key;
s=(node*)malloc(sizeof(pt));
s->val=k;
for(i=1;i<ha[adr].count;i++)
{
pt=pt->next;
}
pt->next=s;
s->next=NULL;
ha[adr].count++;
}
n++;
}
//创建哈希表
void CreateHT(HashTable ha,KeyType x[],int n,int m,int p) {
int i,n1=0;
for(i=0;i<m;i++)
{
ha[i].key=NULLKEY;
ha[i].count=0;
}
for(i=0;i<n;i++)
{
InsertHT(ha,n1,x[i],p);
}
}
void main()
{
int s,d,i;
int sadr;
KeyType x[]={12,4,2,5,7,8};
CreateHT(ha,x,6,100,5);
printf("哈希表创建完成!\n");
while(1)
{
printf("输入查找的关键字:");
scanf("%d",&s);
sadr=SearchHT(ha,5,s);
if(sadr==-1)
printf("查找失败!\n");
else
printf("关键字%d的地址为%d:",s,sadr); printf("输入删除的关键字:");
scanf("%d:",&d);
DeleteHT(ha,5,d,6);
printf("关键字%d删除完成!",d);
printf("输入插入数据:");
scanf("%d",&i);
InsertHT(ha,6,i,5);
}
}。

相关文档
最新文档