PATA1145 Hashing - Average Search Time(坑点说明)

原题链接1145 Hashing - Average Search Time
相似题目PAT甲级1078 Hashing
本题考察哈希表,往里面放置数据以及计算进行探查平均次数。

数据结构

题目告诉所给定的表长都是小于等于10000的,所以,使用数组pos[10010]来记录当前位置是否被占用,初始值为false,以及数组h[10010]来记录当前位置里面放置的数据,初始值全部为-1,使用vector < int > no来记录插不进哈希表中的数据,使用sum来记录总的探查次数,初始值为0。

const int maxn = 10010;
int p[maxn] = {
    
    0};
int h[maxn],sum = 0;//数组h使用memset(h,-1,sizeof(h))初始化
vector<int> no;

表长选取

关于表长选取,原题说是

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.

表长得是大于等于给定表长的最小素数。
所以首先将表长tsize修改为符合要求的数据
使用一个判断素数的函数judge()

bool judge(int n){
    
    
    if(n < 2) return false;//小于2时特判
    int s = sqrt(1.0 * n);
    for(int i = 2;i <= s;i++){
    
    //注意起始为2,终止为s
        if(n % i == 0)
            return false;
    }
    return true;
}

这样就可以先修改tsize

while(!judge(tsize)) tsize++;

放置数据

解决冲突使用的是平方探查法(Quadratic probing),并且只向正向探测。所以,对于当前的值cur,生成它的可能位置pos = cur % tsize,去检测pos是否已经被占用,即是否p[pos] = true,如果是的话,则,进行冲突处理。
关键是冲突处理。平方探查法,进行冲突处理是,要从当前位置pos处开始,不断检查pos +11,pos + 22…处是否放置了数据,即从pos号位置开始,检测距离pos位置整数平方位置是否放置了数据,如果pos + i*i大于tsize,则对tsize进行取模
详细可以参考下面图片
在这里插入图片描述
探测到是么时候结束探测呢?
如果 i 从0增到tsize-1仍然找不到位置,那么对于大于等于tsize的 i 来说,也是找不到位置的
证明如下
在这里插入图片描述
所以说,对于当前的数据cur来说,令 i 从0开始枚举,一直枚举到 tsize - 1,仍然没有找到位置,即对于使用 (cur + i * i) % tsize生成的pos,全部是p[pos] == true,则cur就不能插入到哈希表中。
体现在代码里就是

for(i = 0;i < tsize;i++){
    
    //令i从0开始,到tsize- 1
    int pos = (cur + i * i) % tsize;//生成位置
    if(!p[pos]){
    
    //如果当前位置上没有数据
        p[pos] = true;//将该位置记录为存放了数据
        h[pos] = cur;//将该数据存放到pos处
        break;//打破找位置循环
     }
}
if(i == tsize) no.push_back(cur);//如果i等于tsize,则cur插不进去了

计算进行探查平均次数

本题计算给定的数据的平均探查次数,也就是需要知道,找数据是否存在或者不存在于哈希表中,一共进行多少次探测。对于给定的欲查找数据cur来说,和放置数据一样,使用平方探查法,令 i 从0,增到tsize(不是tsize - 1),即 i 在[0,tsize]中取值,先生成位置pos,如果该位置还没有被放过数据,即p[pos] 为 false,可以直接结束查找,因为这样的话,这个数据肯定不在哈希表中。或者该位置已经放置了数据,且h[pos] == cur,可以直接结束查找了,因为这样的话,这个数据肯定在哈希表中,如果 i 在0 到tsize的范围内都没有找到,则不在哈希表中。

        int i;
        for(i = 0;i <= tsize;i++){
    
    //注意,i 要增到tsize
            int pos = (cur + i * i) % tsize;//生成位置
            sum++;//探查次数 + 1
            if(!p[pos] || h[pos] == cur) break;//如果过程中肯定不存在,打断循环
        }

总结

本题做错主要有以下几点:
1、往哈希表里面插入cur找位置时,位置找错。其实,我使用的是 i 取值为[0,tsize),即 i 取值为从0取到tsize - 1,i 最大是取到tsize还是tsize- 1,这个在给数据查找位置时是无关紧要的,因为,i 为tsize-1时找不到位置,则已经说明cur不可能插入哈希表中了。更不用说 i 取到tsize了。
但是在对于给定的数据进行是否存在与哈希表中进行探测时,i 取到多少就非常重要了,因为,本题中,如果在进行探测时将 i 最大取到tsize - 1,是错误的。
所以无论是将数据插入哈希表还是查找数据是否在表中,i 最好是统一从0 取到 tsize。
2、在进行探测时,如果已经肯定数据不存在与哈希表中或者该数据肯定在哈希表中,要及时终止查找探测。如果该位置还没有被放过数据,即p[pos] 为 false,可以直接结束查找,因为这样的话,这个数据肯定不在哈希表中。或者该位置已经放置了数据,且h[pos] == cur,可以直接结束查找了,因为这样的话,这个数据肯定在哈希表中,

AC代码

#include <iostream>
#include <vector>
#include <cmath>
#include <cstring>

using namespace std;

const int maxn = 10010;
int p[maxn] = {
    
    0};
int h[maxn],sum = 0;
vector<int> no;

bool judge(int n){
    
    
    if(n < 2) return false;
    int s = sqrt(1.0 * n);
    for(int i = 2;i <= s;i++){
    
    
        if(n % i == 0)
            return false;
    }
    return true;
}

int main()
{
    
    
    memset(h,-1,sizeof(h));
    int tsize,n,m;
    scanf("%d%d%d",&tsize,&n,&m);
    while(!judge(tsize)) tsize++;

    for(int r = 0;r < n;r++){
    
    
        int cur;
        scanf("%d",&cur);
        int i;
        for(i = 0;i <= tsize;i++){
    
    //令i从0增加到tsize
            int pos = (cur + i * i) % tsize;//生成位置
            if(!p[pos]){
    
    //位置没有被占用
                p[pos] = true;
                h[pos] = cur;
                break;
            }
        }
        if(i == tsize +1) no.push_back(cur);//i== tsize + 1,则cur插不进去
    }

    for(int r = 0;r < m;r++){
    
    
        int cur;
        scanf("%d",&cur);
        int i;
        for(i = 0;i <= tsize;i++){
    
    //注意,i从0 增加到tsize
            int pos = (cur + i * i) % tsize;//生成位置
            sum++;          //探测次数加1
            if(!p[pos] || h[pos] == cur) break;//如果cur肯定不存在,直接终止
        }
    }

    for(int i = 0;i < no.size();i++) printf("%d cannot be inserted.\n",no[i]);
    printf("%.1f",1.0 * sum / m);//注意是一位小数
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44321570/article/details/114370982