数据结构题目收录(二十一)

本文探讨了散列表中处理冲突的各种方法及其特点,包括开放定址法、链地址法等,并分析了不同方法对查找效率的影响。

1、在开放定址法中散列到同一个地址而引起的“堆积”问题是由于()引起的。

  • A:同义词之间发生冲突
  • B:非同义词之间发生冲突
  • C:同义词之间或非同义词之间发生冲突
  • D:散列表“溢出”
解析

在开放定址法中散列到同一个地址而产生的“堆积”问题,是同义词冲突的探查序列和非同义词之间不同的探查序列交织在一起,导致关键字查询需要经过较长的探测距离,降低了散列的效率。因此要选择好的处理冲突的方法来避免“堆积”。

答案:C

2、 下列关于散列冲突处理方法的说法中,正确的有()。

Ⅰ、采用再散列法处理冲突时不易产生聚集
Ⅱ、采用线性探测法处理冲突时,所有同义词在散列表中一定相邻
Ⅲ、采用链地址法处理冲突时,若限定在链首插入,则插入任一个元素的时间是相同的
Ⅳ、采用链地址法处理冲突易引起聚集现象

  • A:Ⅰ和Ⅲ
  • B:Ⅰ、Ⅱ和Ⅲ
  • C: Ⅲ和Ⅳ
  • D: Ⅰ和Ⅳ
解析
  • 利用再散列法处理冲突时,按一定的距离,跳跃式地寻找“下一个”空闲位置,减少了发生聚集的可能,Ⅰ正确。
  • 散列地址i的关键字,和为解决冲突形成的某次探测地址为i的关键字,都争夺地址i,i+1,…,因此不一定相邻,Ⅱ错误。Ⅲ正确。
  • 同义词冲突不等于聚集,链地址法处理冲突时将同义词放在同一个链表中,不会引起聚集现象,Ⅳ错误。
答案:A

3、 设有一个含有200个表项的散列表,用线性探测法解决冲突,按关键字查询时找到一个表项的平均探测次数不超过1.5,则散列表应能够容纳()个表项(设查找成功的平均查找长度为ASL=[1+1/(1-α\alphaα)]/2,其中α\alphaα为装填因子)。

  • A:400
  • B:526
  • C:624
  • D:676
解析

若有200个表项要放入散列表,采用线性探测法解决冲突,限定查找成功的平均查找长度不超过1.5,则
请添加图片描述

答案:A

4、 假定有K个关键字互为同义词,若用线性探测法把这K个关键字填入散列表,至少要进行()次探测。

  • A:K-1
  • B:K
  • C:K+1
  • D:K(K+1)/2
解析

K个关键字在依次填入的过程中,只有第一个不会发生冲突,故探测次数为(1+2+3+…+K)=K(K+1)/2,即选D。

答案:D

5、 对包含n个元素的散列表进行查找,平均查找长度()。

  • A:为O(log⁡2\log_2log2n)
  • B:为O(1)
  • C:不直接依赖于n
  • D:直接依赖于表长m
解析

在散列表中,平均查找长度与装填因子α\alphaα直接相关,表的查找效率不直接依赖于表中已有表项个数n或表长m。若散列表中存放的记录全部是某个地址的同义词,则平均查找长度为O(n)而非O(1)。

答案:C

6、 采用开放定址法解决冲突的散列查找中,发生聚集的原因主要是()。

  • A:数据元素过多
  • B:负载因子过大
  • C:散列函数选择不当
  • D:解决冲突的方法选择不当
解析

聚集是因为选取不当的处理冲突的方法,而导致不同关键字的元素对同一散列地址进行争夺的现象。用线性再探测法,容易引发聚集现象。

答案:D

7、在采用链地址法处理冲突所构成的散列表上查找某一关键字,则在查找成功的情况下,所探测的这些位置上的键值();若采用线性探测法,则()。

  • A:一定都是同义词
  • B:不一定都是同义词
  • C:都相同
  • D:一定都不是同义词
解析

因为在链地址法中,映射到同一地址的关键字都会链到与此地址相对应的链表上,所以探测过程一定是在此链表上进行的,从而这些位置上的关键字均为同义词;但在线性探测法中出现两个同义关键字时,会把该关键字对应地址的下一个地址也占用掉,两个地址分别记为Addr、Addr+1,查找一个满足H(key)=Addr+1的关键字key时,显然首次探测到的不是key的同义词。

答案:A,B

8、用哈希(散列)方法处理冲突(碰撞)时可能出现堆积(聚集)现象,下列选项中,会受堆积现象直接影响的是()。

  • A:存储效率
  • B:散列函数
  • C:装填(装载)因子
  • D:平均查找长度
解析

产生堆积现象,即产生了冲突,它对存储效率、散列函数和装填因子均不会有影响,而平均查找长度会因为堆积现象而增大,选D。

答案:D

9、现有长度为11且初始为空的散列表HT,散列函数是H(key)=key%7,采用线性探查(线性探测再散列)法解决冲突。将关键字序列87,40,30,6,11,22,98,20依次插入HT后,HT查找失败的平均查找长度是()。

  • A:4
  • B:5.35
  • C:6
  • D:6.29
解析

采用线性探查法计算每个关键字的存放情况如下表所示。
请添加图片描述
由于H(key)=0~6,查找失败时可能对应的地址有7个,对于计算出地址为0的关键字key0,只有比较完0~8号地址后才能确定该关键字不在表中,比较次数为9;对于计算出地址为1的关键字key1,只有比较完1~8号地址后才能确定该关键字不在表中,比较次数为8;以此类推。需要特别注意的是,散列函数不可能计算出地址7,因此有

请添加图片描述

答案:C

10、对任意7个关键字进行基于比较的排序,至少要进行()次关键字之间的两两比较。

  • A:13
  • B:14
  • C:15
  • D:6
解析

对于任意序列进行基于比较的排序,求最少的比较次数应考虑最坏情况。对于任意n个关键字排序的比较次数至少为┌\ulcornerlog⁡2\log_2log2(n!)┐\urcorner。将n=7代入公式,答案为13。

答案:A
1.问题描述 若某单位职工有n名,试以职工的姓名为关键码,设计散列表,请完成相应的建表和查询功能。 2.基本要求 (1)假设每名职工的姓名以汉语拼音形式表示。待填人散列表的人名共有n个。请构造合适的散列函数,选择恰当的处理冲突的方法来构造散 列表。 (2)输入职工的姓名,实现在散列表中检索职工的信息,显示检索的过程。 3.提示与分析 (1)散列函数的构造: 散列函数的选取原则是形式简单而分布均匀。一般情况下,人的姓名包含2-4个汉字, 因此拼音的长度不超过24个字符。一个字的拼音一般由声母和韵母构成,汉语拼音中的声 母和韵母是有限的,因此可以考虑对声母和韵母进行编号,那么一个姓名的编号就是一串数 字,以此数字为关键码,根据职工人数n确定散列表的长度m,采用除留余数法 H(key)=key % p (p选择不超过m的最大素数) 来计算散列地址(当姓名较长时,key位数较多,可先作折叠处理,再计算散列地址)(2)处理冲突的方法: 好的散列函数使得散列地址在散列表中分布较均匀,发生冲突的机会较小,但冲突是不 可避免的,发生冲突时,采用开放定址法处理冲突,可采用二次探测法形成探测序列。 (3)查找过程: 输人要查找的职工的姓名,根据其姓名的编号key 计算散列地址H(key),判断此地址 单元存放的元素的关键码是否等于key,若等于,查找成功;否则,根据探测序列,计算下 一散列地址,判断此地址单元存放的元素的关键码是否等于key,若等于查找成功;否则, 继续计算下一地址,直至查找成功或失败。 4.测试数据 请读者自行选取自己周围较熟悉的60个人的姓名进行测试。 5.选作内容 (1)设计其他几个不同的散列函数,比较它们的地址发生冲突的概率。 (2)在确定散列函数的前提下,尝试其他不同处理冲突的方法。 (3)研究若干人名拼音的特点,寻找更好的散列函数,使得对于不同的拼音名映射到不同的散列地址。
05-07
<think>嗯,用户想用Java实现一个基于职工姓名的散列表,包括建表、冲突处理和查询功能。首先,我需要确定如何设计散列函数。职工姓名是汉语拼音,所以需要考虑如何将拼音字符串转换为散列值。常见的散列函数比如字符串的每个字符的ASCII码相加,然后取模。不过这样可能会有冲突,所以需要处理冲突的方法,比如开放定址法中的线性探测或者二次探测。 然后,用户提到要处理冲突,可能需要选择开放定址法中的具体方法。比如二次探测法可以避免线性探测的聚集问题。那在Java中,如何实现二次探测呢?需要计算下一个位置的索引,可能需要一个循环,逐步增加步长的平方来查找空槽。 接下来是建表和查询功能。散列表的大小应该是一个质数,以减少冲突。比如初始容量设为16,然后当负载因子超过阈值时进行扩容。但用户可能没有提到动态扩容,不过在实际实现中可能需要考虑。不过用户的需求可能先聚焦在基本功能上,所以可以先实现固定大小的散列表,或者简单的扩容。 然后是具体的代码结构。定义一个Employee类,包含姓名和其他信息。散列表可以用一个数组来存储Employee对象,未使用的槽位设为null,已删除的位置可能需要标记,比如使用一个标记对象,避免查找时中断。不过这里可能需要用开放定址法中的状态标记,比如存在、已删除、空。 在散列函数部分,可以将每个字符转换为ASCII码,然后乘以权重,比如31的幂次,类似Java字符串的hashCode方法。然后对表的大小取模,得到初始位置。如果发生冲突,使用二次探测法寻找下一个位置。 查询功能需要根据姓名计算散列值,然后按照同样的探测方法查找,直到找到对应的元素或者遇到空槽。 可能需要注意二次探测的步长计算,比如h(k) + i²,但这样可能导致超出表的大小,所以需要取模。但二次探测的探测序列应该是h(k) + c1*i + c2*i²,这里可能选择c1=0, c2=1。另外,二次探测可能会导致无法找到空槽,即使表未满,所以需要保证表的大小是质数且满足4j+3的形式,这样探测才能覆盖所有位置。 不过用户可能希望示例代码尽量简单,所以可能需要简化处理,比如使用线性探测或者二次探测的基本实现,而不过多考虑表大小的严格条件。 现在,结合这些思考,需要写出具体的Java代码,包括散列函数、冲突处理、插入和查询方法。同时,用户可能需要了解如何测试这个散列表,比如插入几个员工,然后查询是否存在。 另外,用户之前的引用是关于散列表的基本思想,所以需要确保代码中的散列函数将关键字映射到有限的连续区间,并处理冲突。因此,在代码实现中,散列函数的设计和冲突处理是关键部分。 最后,生成相关问题时要围绕散列表的实现细节,比如不同的冲突处理方法比较,如何优化散列函数,动态扩容的实现,以及Java中的其他集合类对比。</think>以下是用Java实现基于职工姓名的散列表的完整方案,包含散列函数设计、二次探测法冲突处理和查询功能的实现: ### 一、实现方案 #### 1. 数据结构设计 ```java class Employee { String name; // 汉语拼音全拼,如"zhangsan" // 其他字段... public Employee(String name) { this.name = name; } } class HashTable { private int capacity; private Employee[] table; private boolean[] deleted; private static final double LOAD_FACTOR = 0.75; public HashTable(int capacity) { this.capacity = nextPrime(capacity); this.table = new Employee[this.capacity]; this.deleted = new boolean[this.capacity]; } } ``` #### 2. 散列函数设计 采用多项式累积法,时间复杂度$O(n)$: ```java private int hash(String key) { int hashVal = 0; for (int i = 0; i < key.length(); i++) { hashVal = 31 * hashVal + key.charAt(i); } return (hashVal & 0x7FFFFFFF) % capacity; } ``` #### 3. 二次探测法实现 ```java private int findIndex(String key) { int hashVal = hash(key); int offset = 1; int initialHash = hashVal; while (table[hashVal] != null || deleted[hashVal]) { if (table[hashVal] != null && table[hashVal].name.equals(key)) { return hashVal; } hashVal = (initialHash + offset * offset) % capacity; offset++; if (offset > capacity) return -1; // 表已满 } return hashVal; } ``` #### 4. 完整操作方法 ```java public void insert(Employee emp) { if ((double)size/capacity >= LOAD_FACTOR) { rehash(); } int index = findIndex(emp.name); if (index != -1) { table[index] = emp; deleted[index] = false; } } public Employee get(String name) { int index = findIndex(name); if (index != -1 && table[index] != null) { return table[index]; } return null; } public void delete(String name) { int index = findIndex(name); if (index != -1 && table[index] != null) { table[index] = null; deleted[index] = true; } } ``` #### 5. 再散列实现 ```java private void rehash() { int newCapacity = nextPrime(2 * capacity); Employee[] oldTable = table; table = new Employee[newCapacity]; capacity = newCapacity; deleted = new boolean[newCapacity]; for (Employee emp : oldTable) { if (emp != null) { insert(emp); } } } ``` ### 二、关键设计要点 1. **散列函数优化**:采用31作为系数,这是Java字符串哈希的标准系数,能有效分散分布[^1] 2. **冲突处理**:二次探测法采用$h(k) + i^2$的探测序列,相比线性探测能减少聚集现象 3. **删除标记**:使用独立布尔数组记录删除状态,避免出现"墓碑"堆积问题 4. **动态扩容**:当装载因子达到0.75时自动扩容,保持操作时间复杂度接近$O(1)$ ### 三、性能测试示例 ```java public static void main(String[] args) { HashTable ht = new HashTable(10); ht.insert(new Employee("zhangsan")); ht.insert(new Employee("lisi")); ht.insert(new Employee("wangwu")); System.out.println(ht.get("zhangsan")); // 成功查询 ht.delete("lisi"); System.out.println(ht.get("lisi")); // 返回null } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员丶星霖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值