散列表平方探测法

/*
 * 平方探测采用原哈希值加整数平方作为备选位置避免了一次聚集
 * 会产生二次聚集,在备选位置上聚集
 * 装填因子不能大于0.5,即至少有一半为空且表大小为素数才能保证插入元素总能成功
 */
public class QuadraticProbingHashTable(){
   public QuadraticProbingHashTable(){
	   this(DEFAULT_TABLE_SIZE);
   }
   public QuadraticProbingHashTable(int size){
	   allocateArray(size);
	   
   }
   private static class HashEntry<AnyType>{
	   public AnyType element;
	   public booblean isActive;
	   public HahEntry(AnyType x){
		   this(e,true);
	   }
	   public HashEntry(AnyType x,boolean i){
		   element=e;
		   isActive=i;
	   }
   }
   private static final int DEFAULT_TABLE_SIZE=11;
   private HashEntry<AnyType>[] array;

   public void makeEmpty(){
	   for(int j=0;j<array.length;j++){
		   array[i].element=null;
		   array[i].isActive=false;
	   }
   }
   public booblean contains(AnyType x){
	   int CurrentPos=findPos(AnyType);
	   return isActive(currentPos);
   }
   public void insert(AnyType){
	   int pos=findPos(x);
	   if(isActive(pos))
		   return;
	   array[pos]=new HashEntry<AnyType>(x,ture);
	   if(++currentPos>array.length/2)
		   rehash();
   }
   /*
    *删除只是将元素标记为删除
	*/
   public void remove(AnyType x){
	   int currentPos=findPos(x);
	   if(isActive(currentPos));
	   array[currentPos].isActive=false;
   }
   private  allocateArray(int arraySize){
	   array=new HashEntry[arraySizee];
   }
   /*
    * HashEntry引用数组有三种情况
	* 1null
	* 2非null,该项是活动的(isActive=true)
	* 3非null,该项标记W为删除(isActive=false)
	*/
   private boolean isActive(int currentSize){
	   return array[currentPos]!=null&array[currentPos].isActive;
   }
   private int findPos(AnyType x){
	   int offset=1;
	   int currentPos=myhash(x);
	   while(array[currentPos]!=null&&!array[currentPos].element.equals(x)){
		   currentPos+=offset;
		   offset+=2;//下一个备选位置和上一个备选位置差2i+1,避免了乘除
		   if(currentPos>=arraySize)
			   currentPos-=arraySize;
		   }
		   return currentPos;
   }
   private int myhash(AnyType x){
	   int hashVal=x.hashCode();//应当是关键字求值
		 hashVal%=theLists.length;
		 if(hashVal<0){
			 hashVal+=theLists.length;
		 }
		 return hashVal;
   }
   /*
    *平方探测再散列
	*/
   private void rehash(){
	   HashEntry<AnyType>[] oldArray=array;
	   allocateArray(nextPrime(2*oldArray.length));
	   currentSize=0;
	   //copy
	   for(int i=0;i<oldArray.length;i++){
		   if(oldArray[i]!=null&&oldArray[i].isActive){
			   insert(oldArray[i].element);
		   }
	   }
   }
   /*
    *@return 包括n在内的之后的第一个素数
	*/
   private static int nextPrime(int n){
		 while(!isPrime(n)){
			 ++n;
		 }
		 return n;
	 }
   /*
    *素数判断
	*/
   private static boolean isPrime(int n){
		 for(int i=2;i<sqrt(n);i++){
			 int k=n%i;
			 if(k==0){
				 return false;
			 }
		 }
		 return true;
	 }
}

   

<think>好的,用户之前让我介绍了散列表,现在他们接着问哈希函数的平方探测法。我需要先回忆一下平方探测法的具体内容,确保自己理解正确。 平方探测法是开放寻址法中解决冲突的一种方法,和链地址法不同,它不在同一个位置用链表,而是寻找下一个可用的槽位。用户可能已经知道开放寻址法的基本概念,但需要详细解释平方探测法的工作原理。 用户可能的应用场景是什么?也许他们正在学习数据结构,或者准备面试,需要了解不同的冲突解决方法。他们可能想知道平方探测法如何操作,有什么优缺点,以及和线性探测法的区别。 接下来要确定回答的结构。首先解释平方探测法的定义,然后举例说明具体步骤,比如如何计算探测序列。需要提到哈希函数的二次函数形式,比如h(k, i) = (h'(k) + c1*i + c2*i²) % m,这里可能需要简化,用常见的例子,比如h(k, i) = (h'(k) + i²) % m。 然后要举例说明插入和查找过程,比如插入键时遇到冲突,如何按平方探测寻找空位。同时要指出平方探测法的优点,比如减少聚集现象,但可能有覆盖不全的问题,比如表的大小需要是质数且满足4k+3,否则可能无法探测所有位置。 还要比较平方探测法和线性探测法的区别,说明各自的适用场景。最后总结应用场景和注意事项,比如负载因子控制,表大小选择等。 需要注意避免过于技术化的术语,保持解释的易懂性。用户可能需要实际例子来理解,所以插入多个键并展示每一步的探测过程会很有帮助。同时需要指出缺点,比如二次探测可能无法遍历所有槽位,导致插入失败的情况,这时候需要重新哈希或扩容。 确保回答结构清晰,分步骤说明,使用示例和对比,让用户全面理解平方探测法的机制和特点。检查是否有错误,比如平方探测的步长是否正确,是否覆盖了所有可能的槽位,或者是否有常见的误解需要澄清。</think>好的!**平方探测法**(Quadratic Probing)是散列表中解决哈希冲突的一种方法,属于**开放寻址法**(Open Addressing)的范畴。其核心思想是:当哈希冲突发生时,通过一个二次函数(平方函数)逐步探测下一个可用的存储位置,直到找到空闲槽位或遍历完整个表。 --- ### **1. 平方探测法的基本思想** - **目标**:解决哈希冲突,无需使用链表(与链地址法不同)。 - **探测序列**:通过以下公式确定下一个探测位置: ``` h(k, i) = (h'(k) + c1*i + c2*i²) % m ``` 其中: - `h'(k)` 是初始哈希函数计算的索引。 - `i` 是探测次数(i=0,1,2,…)。 - `c1` 和 `c2` 是常数(通常取 `c1=0, c2=1`,简化为 `h(k, i) = (h'(k) + i²) % m`)。 - `m` 是散列表的长度。 --- ### **2. 具体示例** 假设散列表长度为 **7**,初始哈希函数为 `h'(k) = key % 7`,使用平方探测法解决冲突(探测公式为 `h(k, i) = (h'(k) + i²) % 7`)。 #### **步骤1:插入键值** 依次插入键:**10, 23, 17, 35, 8**。 1. **插入10** - `h'(10) = 10 % 7 = 3` → 直接存入索引3。 - `h'(23) = 23 % 7 = 2` → 存入索引2。 - 表状态:`[_, _, 23, 10, _, _, _]` 3. **插入17** - `h'(17) = 17 % 7 = 3` → 索引3冲突。 - 探测序列: - i=1: `(3 + 1²) % 7 = 4` → 索引4空闲,存入17。 - 表状态:`[_, _, 23, 10, 17, _, _]` 4. **插入35** - `h'(35) = 35 % 7 = 0` → 存入索引0。 - 表状态:`[35, _, 23, 10, 17, _, _]` 5. **插入8** - `h'(8) = 8 % 7 = 1` → 存入索引1。 - 表状态:`[35, 8, 23, 10, 17, _, _]` #### **步骤2:处理冲突(假设继续插入键25)** - i=1: `(4 + 1²) % 7 = 5` → 索引5空闲,存入25。 - 表状态:`[35, 8, 23, 10, 17, 25, _]` --- ### **3. 平方探测法的特点** #### **优点**: 1. **减少聚集现象**: 相比线性探测法(步长固定为1),平方探测法的步长按平方增长(1,4,9,…),能更分散地探测空闲位置,避免“一次聚集”(大量元素集中在某块区域)。 2. **空间效率高**: 无需额外存储链表,所有数据均存储在数组中。 #### **缺点**: 1. **二次聚集**: 若不同键的初始哈希值相同,探测路径会完全一致,导致“二次聚集”。 2. **可能无法找到空位**: 平方探测法不一定能遍历所有槽位,需满足以下条件之一: - 表长度 `m` 是质数,且负载因子 ≤ 0.5。 - 表长度 `m` 是形如 `4k+3` 的质数(如7, 11, 19等),此时能覆盖所有槽位。 --- ### **4. 与线性探测法的对比** | **对比项** | **线性探测法** | **平方探测法** | |------------------|----------------------------|------------------------------| | **步长** | 固定步长(如+1) | 按平方增长(1,4,9,…) | | **聚集现象** | 容易产生“一次聚集” | 减少聚集,但可能“二次聚集” | | **遍历覆盖性** | 总能遍历所有槽位 | 需满足特定条件(如表长为质数)| | **适用场景** | 负载因子较低时 | 表长设计合理时 | --- ### **5. 应用场景** - 内存受限的场景(无需链表开销)。 - 负载因子较低(通常需 ≤ 0.5)且表长设计为质数。 - 需要平衡插入、查找效率与空间利用率时。 --- ### **总结** 平方探测法通过二次函数生成探测序列,平衡了冲突解决的效率与空间占用。其核心挑战是合理设计表长度(如质数)和控制负载因子,以确保探测路径能覆盖所有槽位。实际应用中需结合场景选择冲突解决策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值