题目大意:
给出散列表长MSize和欲插入的元素,将这些元素按读入的顺序插入散列表中,其中散列函数为H(key)= key %MSize,解决冲突采用只往正向增加的次探查法(即二次方 探查法)。另外,如果题目给出的MSize不是素数,那么需要将MSize重新赋值为第一个比MSize大的素数再进行元素插入。
算法思路:
直接模拟hash映射的过程,首选对于输入的MSize如果不是素数就找到第一个比它大的素数,然后就对每一个输入的数据进行hash映射,使用pos记录映射的位置,如果hashTable[pos]!=0说明当前位置存在冲突,那么就使用二次方探测法进行rehash,如果找到空位后退出循环,如果rehash的次数大于或者等于hash表的长度就表明没有合适的空位也退出循环。这里使用step记录当前存在hash冲突的时候rehash的次数。退出循环后判断step是否大于等于MSize,如果是输出"-",否则输出pos,并且将pos对应位置设置为num。最后注意输出空格。判断一个数字N是否为素数的方法如下:
方法一:
根据素数的定义可知,素数是指大于1,且只能被1和它自身整除的自然数。1不是素数。根据乘法的特性,可知若从2开始遍历到被判断数的平方根都没有找到能被整除的数,则这个数一定为素数。对应代码如下:
bool isPrime(int N){
if(N<=1) return false;
for (int i = 2; i <= sqrt(N * 1.0); ++i) {
if(N%i==0) return false;
}
return true;
}
方法二:
对于一个大于等于5的素数,其特点是总是等于6x-1
和6x+1
,其中 x 是大于等于1的自然数.那么根据这个特点,得到的结论为,在6的倍数两边的数字不一定是素数,但是不在6的倍数两边的数字一定不是素数,这样就可以优化上面的代码,思路就是首先特判小于4的数字,只有2和3才是素数,然后对于所有对6整除余数不为1和5的说明不在6的倍数的两边,一定不是素数,最后从5开始,步长为6进行遍历每一个在6两边的数字,对于可以对于6的倍数两边的数字可以整除的一定不是素数。代码如下:
bool isPrime(int N){
if(N<=3){
return N>1;//2和3才是素数
}
if(N%6!=1&&N%6!=5) return false;
for (int i = 5; i <= sqrt(N * 1.0); i+=6) {
if(N%i==0||N%(i+2)==0) return false;
}
return true;
}
对于rehash次数只需要MSize次的证明(思否的公式编辑存在bug,预览的时候没问题,再发布就有乱码了,下面是预览的截图)
注意点:
1、对于测试点0和3错误的情况,可以仔细看看是不是二次方探测的方法写错了,正确写法是pos = (num+step*step)%MSize;
而不是pos = (pos+step*step)%MSize;
2、如果二次方探测一直没有找到空位,得使用step控制rehash的次数不超过MSize,否则测试点3超时。
3、如果错误的判断1也是素数的话,测试点1会出错。
提交结果:
AC代码;
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
// 判断N是否为素数
//bool isPrime(int N){
// if(N<=1) return false;
// for (int i = 2; i <= sqrt(N * 1.0); ++i) {
// if(N%i==0) return false;
// }
// return true;
//}
bool isPrime(int N){
if(N<=3){
return N>1;//2和3才是素数
}
// 不在6的倍数两边的数字一定不是素数
if(N%6!=1&&N%6!=5) return false;
for (int i = 5; i <= sqrt(N * 1.0); i+=6) {
if(N%i==0||N%(i+2)==0) return false;
}
return true;
}
int hashTable[100000] = {};// hash表
int main(){
int MSize,N;// hash表的大小和插入的数字个数
scanf("%d %d",&MSize,&N);
// 找到第一个大于等于MSize素数
while (true){
if(isPrime(MSize)) break;
++MSize;
}
int num;// 输入的每一个数字
for (int i = 0; i < N; ++i) {
scanf("%d",&num);
int pos = num%MSize;// 插入的位置
int step = 1;// 二次探方的步长
while (hashTable[pos]!=0&&step<MSize){
// 存在hash冲突
pos = (num+step*step)%MSize;
++step;
}
if(step>=MSize){
// 没有空位
printf("-");
} else {
printf("%d",pos);
hashTable[pos] = num;
}
if(i<N-1) printf(" ");
}
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。