算法笔记算法初步(4.2散列)

一、使用散列思想的查询

1.给出N个整数,在给出M个数,问这M个数中的每个数是否在N个数中出现过

思路:   设定布尔型的数组

#include<stdio.h>
const int maxn = 100010;
bool hashArr[maxn] = {false};//用空间交换时间,而非对每个待查询的数遍历所有的数
//所谓hash是形成一个线性表,时间复杂度O(m+n)
int main(){
   int n,m;
   int x;
   scanf("%d",&n);
   for(int i=0;i<n;i++){
    scanf("%d",&x);
    hashArr[x] = true;//标记是否存在
   }
   scanf("%d",&m);
   int a;
   for(int i=0;i<m;i++){
        scanf("%d",&a);
     if(hashArr[a]==true){
        printf("yes\n");
     }
     else{
        printf("no\n");
     }
   }
    return 0;
}

2.进一步 我想知道每个数出现的次数

思路: int 型数组 对出现次数进行计数

#include<stdio.h>
const int maxn = 100010;
int hashTable[maxn] = {0};
int main(){

    int n,m;
    scanf("%d %d",&n,&m);
    int x;
    for(int i=0;i<n;i++){
        scanf("%d",&x);
        hashTable[x]++;//记录出现次数
    }
    int a;
    for(int i=0;i<m;i++){
        scanf("%d",&a);
        printf("%d\n",hashTable[a]);
    }
    return 0;

}

二、散列

上述思路中,我们直接把输入的数作为数组下标,来对这个数的性质进行统计。但是我们无法保证输入的范围,可能它超出我们的表示范围,或者为一个字符串。这时,我们提出散列,它本质是一种转换过程,将元素通过一个函数转换为整数,使得该整数功能如数组下标,尽可能地唯一标识这个元素。

常用的散列函数:

线性变换,平方取中法,除留取余法(较常用  H(key) = key % mod ,mod 一般取为素数);

冲突: key1 不等于 key2  , H(key1) = H(key2)

解决冲突:线性探查法(hash值加一),平方探查,链地址法(将H(key)相同的key连接一条链表)

三、hash 初步

将字符串转换为唯一的整数

预备知识:A - Z 转换为0-25 (x-'A');字符转换为数字 (x-'0')

3.1 假设字符串中只含有大写字母

int hashFunc(char a[],int len){//只含有大写字母的情况
    //字母A - Z 0-25  相当于26进制  ,把它转换为10进制
    int sum = 0;
    for(int i=0;i<len;i++){
        sum = sum * 26 + (a[i] - 'A');
        //a[i] - 'A'  0--25
    }
    return sum;
}

同时含有大小写字母

int hashFunc2(char a[],int len){//含有大小写字母,A-Z 0-25  a-z  26-51
    //52进制转换为十进制
    int id = 0;
    for(int i=0;i<len;i++){
        if(a[i]>='A' && a[i] <='Z'){
            id = id * 52 + (a[i]-'A');
        }
        else if(a[i]>='a'&&a[i]<='z'){
            id = id * 52 + (a[i]-'a'+ 26);
        }
    }
    return id;
}
int hashTable(char a[],int len){
    //形如BCD4 三位字符 加一个数字  -> 采用拼接
    int id=0;
    for(int i=0;i<len;i++){
        id = id * 26 + (a[i]- 'A');
    }
    id = id*10 + (a[len-1]-'0');//注意字符形式转换为整数
    retrun id;
}

给出N个字符串,查询M的字符串(由三个大写字母组成)在N个字符串中出现的次数

查询时也要相应 转化

#include<stdio.h>
#include<string.h>
const int maxn = 100;
char str[maxn][5];
char tmp[5];
int hashArr[26 * 26 * 26 + 10]={0};
int hashTable(char a[],int len){
    int id =0;
    for(int i=0;i<len;i++){
        id  = id * 26 + (a[i]-'A');
    }
    return id;
}
int main(){
    int n,m;
    scanf("%d",&n);
    for(int i=0;i<n;i++){

        scanf("%s",tmp);
      int len1 = strlen(tmp);
        int num = hashTable(tmp,len1);//
        hashArr[num]++;

    }
    scanf("%d",&m);
    for(int i=0;i<m;i++){
        scanf("%s",tmp);
         int len2 = strlen(tmp);
        int num = hashTable(tmp,len2);//
        printf("%d\n",hashArr[num]);

    }
    return 0;


}

  2020/05/03

codeup题目整理  题意转化

问题A:谁是你的潜在朋友 链接

共同朋友 转换为 ->即为求每个书对应出现的次数

#include<stdio.h>
#include<string.h>

int cnt[210] = {0};//计数
int main(){
    int n,m;
    int a[210]={0};//书的映射
    while(scanf("%d %d",&n,&m)!=EOF){
        memset(cnt,0,sizeof(cnt));//无数遍了!!!计数器清零
        for(int i=0;i<n;i++){

            scanf("%d",&a[i]);

            cnt[a[i]]++;
        }
        for(int i=0;i<n;i++){
            if(cnt[a[i]]>1){
                printf("%d\n",cnt[a[i]]-1);
            }
            else{
                printf("BeiJu\n");
            }
        }
    }


    return 0;
}

问题B:分组统计  较难

题目描述

先输入一组数,然后输入其分组,按照分组统计出现次数并输出,参见样例。

输入

输入第一行表示样例数m,对于每个样例,第一行为数的个数n,接下来两行分别有n个数,第一行有n个数,第二行的n个数分别对应上一行每个数的分组,n不超过100。

输出

输出m行,格式参见样例,按从小到大排。

样例输入 Copy

1
7
3 2 3 8 8 2 3
1 2 3 2 1 3 1

样例输出 Copy

1={2=0,3=2,8=1}
2={2=1,3=0,8=1}
3={2=1,3=1,8=0}

1.使用二维数组建立关系,每个组 组中的元素散列计数

2.排序 并除去重复的元素

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 2010;
int cnt[maxn][maxn] = {0};

int x[110] = {0};
int g[110] = {0};

//由小到大排序 并合并重复元素
int del(int a[],int num){
    sort(a,a+num);
    int i,j;
    for(i=0,j=1;j<num;j++){
        if(a[i]!=a[j]){//后面的不重复则继续推进
            a[++i] = a[j];  //a[1]
        }
    }
    return i+1;


}



int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        int num = 0;
        for(int i=0;i<n;i++){
          memset(cnt,0,sizeof(cnt));
            scanf("%d",&num);
            for(int j=0;j<num;j++){
                scanf("%d",&x[j]);
            }
            for(int j=0;j<num;j++){//组和元素如何对应并完成分组的计数
                scanf("%d",&g[j]);
                 cnt[g[j]][x[j]]++;//使用二维数组建立关系,对应关系下每个组中元素的计数
            }
            int g0 = del(g,num);
            int x0 = del(x,num);

            for(int i=0;i<g0;i++){
                printf("%d",g[i]);
                printf("={");
                for(int j=0;j<x0;j++){
                    printf("%d=%d",x[j],cnt[g[i]][x[j]]);
                    if(j<x0-1) printf(",");

                }
                printf("}\n");
            }

        }


    }

    return 0;
}

问题 C: Be Unique (20)  题目

所谓unique 就是出现次数为一即可,不用想着去把相同的元素删除掉,而是 唯一出现的元素最终输出出来

#include<stdio.h>
#include<string.h>
const int maxn = 100000;
int ex[maxn]={0};
int a[maxn] = {0};
int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        memset(ex,0,sizeof(ex));

        int x;
        for(int i=0;i<n;i++){
            scanf("%d",&x);
            a[i] = x;

            ex[a[i]]++;

        }
        int flag = 0;
        for(int i=0;i<n;i++){
            if(ex[a[i]] == 1){//唯一  次数为1
                flag = 1;
                printf("%d\n",a[i]);
                break;
            }
        }
        if(flag==0){
            printf("None\n");
        }


    }
    return 0;

}

问题 D: String Subtraction (20)  题目

将大字符串a 中,出现在小字符串b中的字符去掉   使用hash 思路标记某个字母是否存在即可

#include<stdio.h>
#include<string.h>
 char a[10010];
 char b[10010];
int main(){

    gets(a);
    gets(b);
    bool b0[300] = {false};
    for(int i=0;i<strlen(b);i++){
        b0[b[i]] = true;//在或不在
    }
    for(int j=0;j<strlen(a);j++){

            if(b0[a[j]]==false){
               printf("%c",a[j]);
            }

    }
    printf("\n");

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值