一、使用散列思想的查询
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;
}