散列(hash)练习题

在哈希这一块常用的问题包括:判断<=105个正整数中某m个正整数是否出现过、出现了多少次——声明bool/int hashTable[maxn] = {false}/{0}。其做法本质是直接将输入的整数作为数组的下标来对这个数的性质进行统计,即hash(key)=key直接定址,是最常见也最实用的散列应用。复杂一点还有二次映射。把这两个思想掌握了就差不多了。
本篇文章有的内容来自 https://blog.csdn.net/myRealization/article/details/80154726

谁是你的潜在朋友 【★】

http://codeup.cn/problem.php?cid=100000582&pid=0

  • 题目描述
    “臭味相投”——这是我们描述朋友时喜欢用的词汇。两个人是朋友通常意味着他们存在着许多共同的兴趣。然而作为一个宅男,你发现自己与他人相互了解的机会 并不太多。幸运的是,你意外得到了一份北大图书馆的图书借阅记录,于是你挑灯熬夜地编程,想从中发现潜在的朋友。
    首先你对借阅记录进行了一番整理,把N个读者依次编号为1,2,…,N,把M本书依次编号为1,2,…,M。同时,按照“臭味相投”的原则,和你喜欢读同一本书的人,就是你的潜在朋友。你现在的任务是从这份借阅记录中计算出每个人有几个潜在朋友。

  • 输入
    每个案例第一行两个整数N,M,2 <= N ,M<= 200。接下来有N行,第i(i = 1,2,…,N)行每一行有一个数,表示读者i-1最喜欢的图书的编号P(1<=P<=M)

  • 输出
    每个案例包括N行,每行一个数,第i行的数表示读者i有几个潜在朋友。如果i和任何人都没有共同喜欢的书,则输出“BeiJu”(即悲剧,^ ^)

样例输入 
4 5
2
3
2
1
样例输出	
1
BeiJu
1
BeiJu

用数组做下标,直接定址hash

#include<cstdio>
int main(void)
{
    
    
	int M,N;
	int i;
	while(scanf("%d%d",&N,&M)!=EOF)
	{
    
    
	    int a[M+1]={
    
    0};
	    int student[N+1]={
    
    0};
		for(i=1;i<=N;i++)
		{
    
    
			scanf("%d",&student[i]);
			a[student[i]]++;
		}
		for(i=1;i<=N;i++)
		{
    
    
			if( (a[student[i]]-1) == 0 )
			{
    
    
				printf("BeiJu\n");
				continue;
			}
			printf("%d\n",a[student[i]]-1);
		}
	}
	return 0;
}

是唯一的 【★】

  • 题目描述
    对火星上的人们来说,独特是如此重要,以至于他们的彩票也是以一种独特的方式设计的。获胜的规则很简单:一次下注是从[1,104]中选择一个数字。第一个押注于唯一号码的人获胜。例如,如果有7人在5 31 5 88 67 88 88 17上下注,那么第二个押注31的人就赢了。
  • 输入
    每个输入文件包含一个测试用例。每一种情况都包含以正整数N开头的一行(N<=105),然后是N次下注。数字用空格分隔。
  • 输出
    对于每个测试用例,用一行打印获胜号码。如果没有赢家,则打印“无”。
样例输入
7 5 31 5 88 67 88 17
5 888 666 666 888 888
样例输出
31
None

用数组做下标,直接定址hash

#include<cstdio>
int a[10005];
int main(void)
{
    
    
	int N;
	int i;
	int flag;
	while(scanf("%d",&N)!=EOF)
	{
    
    
		flag=0;
	    int student[N+1]={
    
    0};
		for(i=1;i<=N;i++)
		{
    
    
			scanf("%d",&student[i]);
			a[student[i]]++;
		}
		for(i=1;i<=N;i++)
		{
    
    
			if( a[student[i]] == 1 )
			{
    
    
				printf("%d\n",student[i]);
				flag=1;
				break;
			}
		}
		if(!flag)
		{
    
    
			printf("None\n");
		}
	}
	return 0;
}

字符串减法 【★★】

  • 题目描述
    给定两个字符串S1和S2,S=S1-S2定义为S中所有字符之后的剩余字符串。2来自S1。你的任务只是计算S1-S2对于任何给定的字符串。然而,做这件事可能并不那么简单。快地.
  • 输入
    每个输入文件包含一个测试用例。每个案例包含两行,给出S1和S2分别。这两个字符串的字符串长度不超过10。4。保证所有字符都是可见的ASCII代码和空格,一个新的行字符表示字符串的结束。
  • 输出
    对于每个测试用例,打印S1-S2一条线。
样例输入
They are students.
aeiou
样例输出
Thy r stdnts.

用数组做下标,直接定址hash

#include<cstdio>
#include<cstring>
char s1[1005],s2[1005];
bool hush[130]={
    
    false};
int main(void)
{
    
    
	int i=0;
	while( gets(s1) != NULL)
	{
    
    
		gets(s2);
		for(i=0;i<strlen(s2);i++)
		{
    
    
			hush[s2[i]]=true;//确定重复
		}
		for(i=0;i<strlen(s1);i++)
		{
    
    
			if(hush[s1[i]]==false)
				printf("%c",s1[i]);
		}
	}
	return 0;
}

分组统计【★★★】

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

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

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

样例输入 
1
7
3 2 3 8 8 2 3
1 2 3 2 1 3 1
样例输出	
1={2=0,3=2,8=1}
2={2=1,3=0,8=1}
3={2=1,3=1,8=0}

解析:这一道题目可以说是集整数哈希思想于大成,值得仔细分析。

  • 首先,题目中说输入的数据量不超过100,类别同样不超过100,但是没有说数据的具体范围,可能会在整数作为数组的下标超过数组下标范围,理论上来说,对于这种题目,以空间换时间,要求数据应该不超过106。因此,统计输入的数据和组别是否已经出现,可以开hashTable[100100]这样大。
  • 去重操作。在有了标识数据是否出现的哈希表的情况下,可以很简单的做到。
    答案矩阵是一个二次映射的二维数组,可以直接通过组别和数据查到相应组别的数据出现的次数。
    出现50%错误:如果二维数组开的太小,很容易溢出,所以记录第一行数据中最大的值,将其+10作为二维数组中的列数即可解决。

思路:

  1. 输入第一行数据,存入data[]中,同时使用hashTable1[]去重,将去重后的数存入nums[]中,并排序。
  2. 输入第二行组别,存入group[]中,同时使用hashTable2[]函数去重,将去重后的数存入g[]中,并排序。
  3. 同时,将group[]和data[]对应映射到ans[][]中,ans[i][j]中i为分组,j为数。a[i][j]为第i组的j数出现的次数。
  4. 遍历输出。
说白了就是用一个数组输入第一行的数。
再用一个数组保存去重后的数。然后排序。

第二行也是同理的。
最后用一个映射的二维数组统计出现的次数。

用数组做下标,直接定址hash,和二次映射的hash

#include <cstdio>
#include <algorithm>
using namespace std;

int main() {
    
    
	int m;
	scanf("%d", &m);
	while (m--) {
    
    
		int n, maxCol = 0, data[110], group[110]; //分别记录输入的数据和分组
		scanf("%d", &n);
		
		//nums记录输入的数据去重后的数据  
		int nums[120], len1 = 0;
		//使用哈希表对data进行存在标识, 以便去重 
		bool hashTable1[100100] = {
    
    false}; 
		for (int i = 0; i < n; i++) {
    
     
			scanf("%d", &data[i]);      //边录入边去重 
			if (!hashTable1[data[i]]) {
    
     //如果这个数据尚未被记录 
	    		nums[len1++] = data[i]; 
	            hashTable1[data[i]] = true; 
			}
			//得到最大的数, 方便答案直接映射而不溢出 
			if (maxCol < data[i]) maxCol = data[i];  
		} 
		sort(nums, nums + len1); //数据从小到大存放在nums中, 无重复  
		
		//g记录输入的组别去重后的数据  
		int g[120], len2 = 0; 
		//使用哈希表对group进行存在标识, 以便去重
		bool hashTable2[100100] = {
    
    false};
		/*二维答案表,元素ans[g[i]][nums[j]]表示g[i]组中对应的nums[j]出现的次数 
		ans[i][j], i为分组, j为数, a[i][j]为第i组的j数出现的次数 */
		int ans[n + 10][maxCol + 10] = {
    
    0};  
		for (int i = 0; i < n; i++) {
    
    
			scanf("%d", &group[i]);
			ans[group[i]][data[i]]++; 
			if (!hashTable2[group[i]]) {
    
     //如果这个组别尚未被记录 
			   g[len2++] = group[i];  
			   hashTable2[group[i]] = true;
            } 
		}
		sort(g, g + len2); //组别从小到大存放在g中, 无重复 
	
		//输出结果 
		for (int i = 0; i < len2; i++) {
    
    
			printf("%d={", g[i]);
			for (int j = 0; j < len1; j++) {
    
    
				printf("%d=%d", nums[j], ans[g[i]][nums[j]]);
				if (j < len1 - 1) printf(",");
				else printf("}\n");
			}
		} 
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46527915/article/details/114678579