PTA 11-散列2 Hashing (25分)

本文深入探讨了使用二次探测算法解决哈希表冲突问题的原理与实践,详细介绍了算法的具体实现步骤,并通过示例代码展示了如何根据用户定义的表大小和输入数字数量,将一系列唯一的正整数插入哈希表并输出它们在表中的位置。重点讨论了平方探测法在特定素数表大小下的应用情况及如何判断元素无法放入表中的条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

The task of this problem is simple: insert a sequence of distinct positive integers into a hash table, and output the positions of the input numbers. The hash function is defined to be H(key) = key \% TSizeH(key)=key%TSize where TSizeTSizeis the maximum size of the hash table. Quadratic probing (with positive increments only) is used to solve the collisions.

Note that the table size is better to be prime. If the maximum size given by the user is not prime, you must re-define the table size to be the smallest prime number which is larger than the size given by the user.

Input Specification:

Each input file contains one test case. For each case, the first line contains two positive numbers: MSizeMSize (\le 10^4104) and NN (\le MSizeMSize) which are the user-defined table size and the number of input numbers, respectively. Then NN distinct positive integers are given in the next line. All the numbers in a line are separated by a space.

Output Specification:

For each test case, print the corresponding positions (index starts from 0) of the input numbers in one line. All the numbers in a line are separated by a space, and there must be no extra space at the end of the line. In case it is impossible to insert the number, print "-" instead.

Sample Input:

4 4
10 6 4 15

Sample Output:

0 1 4 -




—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————这道题目我遇到的一点问题:

对于平方探测,如果散列表长度是4k+3形式的素数,平方探测法可以探查到整个散列空间,但是这里的题目给出的是素数,因此存在散列表内即使有空间,也不能放进去的情况,我想的时候主要是在考虑什么情况下才能判断这个元素即使增量一直增大也不能放进去呢?

后来看了下别人写的,自己再推了下:

假设散列表大小为Size,那么当增量大于等于Size平方的时候就是在重复之前1平方,2平方...到(size-1)平方的事情。只要把Size平方,(Size+1)平方因式分解一下,就会发现对求余有影响的就是Size加的部分,后面就是一直重复了。所以有了:

if(CNum>=H->TableSize)
		{
			NewPos=-1;
			break;
		}
NewPos=-1表示不存在这个位置。

还有一点就是:测试点中有一个是输入 1 1,那么对于你求素数那部分的函数,必须把1的特殊情况也考虑进去。

下面给出AC的代码

#include "stdio.h"
#include "stdlib.h"
#define ElementType int
typedef unsigned int Index;
int flag=1;                //标志位,输出用
enum EntryType
{
	Legitimate,Empty,Deleted
};

typedef struct HashEntry Cell;
struct HashEntry
{
	ElementType Element;
	enum EntryType Info;
};

typedef struct HashTbl* HashTable;
struct HashTbl
{
	int TableSize;
	Cell* TheCells;
};
int isPrime(int x)
{
	if (x == 1)return 0;  
	for (int i = 2; i < x; i++)
		if (x % i == 0)
			return 0;  
	return 1;  
}  
int NextPrime(int x)
{
	while (!isPrime(x))
		x++;  
	return x;  
}  

HashTable CreateHash(int N)         //创建一个空的hashtable
{
	int i;
	HashTable H;
	H=(HashTable)malloc(sizeof(struct HashTbl));
	H->TableSize=NextPrime(N);
	H->TheCells=(Cell*)malloc(H->TableSize*sizeof(Cell));
	for(i=0;i<H->TableSize;i++)
		H->TheCells[i].Info=Empty;
	return H;
}

int Hash(int key,int p)
{
	return key%p;
}

Index Find(ElementType Key,HashTable H)
{
	Index CurrentPos,NewPos;
	int CNum=0;               //记录冲突次数
	NewPos=CurrentPos=Hash(Key,H->TableSize);
	while((H->TheCells[NewPos]).Info!=Empty&&H->TheCells[NewPos].Element!=Key)
	{
		CNum++;
		NewPos=(CurrentPos+CNum*CNum)%H->TableSize;
		if(CNum>=H->TableSize)
		{
			NewPos=-1;
			break;
		}
	}

	return NewPos;
}

void Insert(ElementType Key,HashTable H)
{
	Index Pos=Find(Key,H);
	if(flag)       
	{
		if(Pos==-1)
			printf("-");
		else
		{
			H->TheCells[Pos].Info=Legitimate;
			H->TheCells[Pos].Element=Key;
			printf("%d",Pos);
		}
		flag=0;
		return;
	}
	else
	{
		if(Pos==-1)
			printf(" -");
		else
		{
			H->TheCells[Pos].Info=Legitimate;
			H->TheCells[Pos].Element=Key;
			printf(" %d",Pos);
		}
		return;
	}
}
int main()
{
	int i,M,N,element;
	HashTable H;
	scanf("%d%d",&M,&N);
	H=CreateHash(M);
	for(i=0;i<N;i++)
	{
		scanf("%d",&element);
		Insert(element,H);
	}
	return 0;
}



### 哈希查找的平均查找长度析 哈希查找是一种高效的查找方法,其性能取决于多个因素。当讨论哈希查找的平均查找长度时,主要考虑的是冲突处理机制以及负载因子的影响。 #### 负载因子与冲突率的关系 负载因子 \( \alpha = \frac{n}{m} \),其中 \( n \) 是存储在哈希表中的元素数量,\( m \) 是哈希表槽数量。随着负载因子增加,发生冲突的概率也会增大,这会直接影响到查找效率[^1]。 #### 成功查找的平均比较次数 对于成功的查找操作,在理想情况下(即没有冲突),每次查找只需要一次访问即可完成。然而实际上由于存在碰撞,因此需要额外计算探查序列长度。如果采用开放地址法,则成功查找的期望时间复杂度可以表示为: \[ E_{\text{success}}(\alpha)=\begin{cases} \sum^{i=0}_{k}\left(1-\alpha+\alpha k\right)\cdot p(k), & \text{(线性探测)} \\ \ln{\frac{1}{1-\alpha}},& \text{(二次探测/双重散列)} \end{cases} \] 这里 \(p(k)\) 表示第 \(k\) 次尝试找到目标键值的概率布函数[^3]。 #### 失败查找的平均比较次数 对于未存在于哈希表内的关键字进行查找称为失败查找。此时无论哪种方式实现,都需要遍历整个可能的位置直到确认不存在该记录为止。理论上讲,最坏情况下的查找成本接近于满表扫描的成本。但在实际应用中,通常假设均匀随机布模型下估算失败查找所需的时间开销: \[E_{\text{failure}}=\frac{1}{1-\alpha}\] 此表达式适用于大多数常见的解决冲突的方法,比如链地址法、线性探测等[^2]。 ```python def expected_search_cost(alpha, method='linear'): """ 计算给定负载因子α条件下不同策略的成功和不成功查找预期代价 参数: alpha (float): 负载因子 α=n/m method (str): 解决冲突的方式 ('chain', 'linear') 返回: tuple(float,float): 别对应成功查找和失败查找的预计花费 """ import math if method == "chain": success = 1 + alpha / 2 failure = 1 + alpha elif method == "quadratic" or method == "double_hashing": success = math.log(1/(1-alpha)) failure = 1 / (1 - alpha) else: # 默认为 linear probing from scipy.stats import geom pmf = lambda k : ((1 - alpha)**k)*alpha success = sum([(1-(alpha+(alpha*k)) * geom.pmf(k+1, alpha)) for k in range(int(1//alpha)+1)]) failure = 1 / (1 - alpha) return round(success,4),round(failure,4) print(expected_search_cost(.7,'linear')) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值