1078 Hashing (25 分)
题意
给出散列表长TSize和欲插入的元素,将这些元素按读入的顺序插入散列表中,其中散列函数为H(key) = key % TSize,解决冲突采用只往正向增加的二次探查法(即二次方探查法)。
另外,如果题目给出的TSize不是素数,那么需要将TSize重新赋值为第一个比TSize大的素数再进行元素插入。
样例解释
TSize为4,所以需要先找到比4大的第一个素数(即5),于是TSize = 5。接着插入四个元素:
key = 10,H(10) = 10%5 = 0,因此将10插入0号位。
key = 6,H(6) = 6%5 = 1,因此将6插入1号位。
key = 4,H(4) = 4%5 = 4,因此将4插入4号位。
key = 15,H(15) = 15%5 = 0,发生冲突;采用二次方探查法,H(15+1x1)=1,发生冲突;H(15+2x2)=4,发生冲突;H(15+3x3)=4,发生冲突;H(15+4x4)= 1,发生冲...可以证明,后面找不到一个不冲突的时点。
思路
- 首先,对于-一个输入的TSize,如果不是素数,则必须找到第一个比它大的素数。判断一个整数n是否是素数的方法:如果n不能被从2 ~Vn中的每-一个数整除,那么n是素数。
- 开一个bool型数组hashTable[],hashTable[key] == false 表示key号位未被使用。
对每个插入的元素a,计算H(a)并判断对应位置是否被使用。- 如果未被使用,那么就找到可以插入的位置,并输出。
- 如果已被使用, 根据二次方探查法,令步长step初值为1,然后令下一个检测值为(a+step * step) % TSize,判断该位置是否已被占用;如果已被占用,则令step++,再进行判断。step当自增达到TSize时如果还没有找到可用位置,则表明这个这个元素无法被插入(证明见注意点)。
注意点
- Quadratic probing是指二次方探查法,即当H(a)发生冲突时,让a按\(a+1^2,a-1^2,a+2^2,a-2^2,a+3^2,a-3^2...\)的顺序调整a的值。本题中已经说明只要往正向解决冲突,因此需要按\(a+1^2,a+2^2,a+2^3,...\)的顺序调整a的值
- 冲突处理公式是\(M=(a+step*step)%TSize\),注意要模上TSize。
- 1号测试点“答案错误”的原因是:判断质数时把1也当成了质数。
- 证明如果step从0 ~ TSize-1进行枚举却仍然无法找到位置,那么对step大于等于TSize来说也不可能找到位置(即证明循环节为TSize)。
这里只需要证明当step取TSize至2TSize-1也无法找到位置即可:
设\(0 \le x \lt TSize\),那么
(a + (TSize + x) * (TSize + x)) % TSize
=(a + TSize * TSize+ 2 * TSize * x + x * x) % TSize
=(a + x * x) % TSize + TSize * TSize % TSize + 2 * TSize * x % TSize
=(a + x * x) % TSize
由于所有循环节为TSize,如果step从0 ~ TSize-1进行枚举却仍然无法找到位置,那么对step大于等于TSize来说也不可能找到位置。
const int N=1e4+10;
bool vis[N];
int n,Tsize;
int find(int key)
{
int k=key%Tsize;
for(int i=0;i<Tsize;i++)
{
int t=(k+i*i)%Tsize;
if(!vis[t]) return t;
}
return -1;
}
bool isprime(int x)
{
if(x<2) return false;
for(int i=2;i*i<=x;i++)
if(x % i == 0)
return false;
return true;
}
int main()
{
cin>>Tsize>>n;
while(!isprime(Tsize)) Tsize++;
for(int i=0;i<n;i++)
{
int x;
cin>>x;
int k=find(x);
if(k != -1)
{
vis[k]=true;
if(i) cout<<' '<<k;
else cout<<k;
}
else cout<<' '<<'-';
}
cout<<endl;
//system("pause");
return 0;
}