眼红的Medusa

题目描述

虽然 Miss Medusa 到了北京,领了科技创新奖,但是她还是觉得不满意。原因是:她发现很多人都和她一样获了科技创新奖,特别是其中的某些人,还获得了另一个奖项------特殊贡献奖。而越多的人获得了两个奖项,Miss Medusa 就会越眼红。于是她决定统计有哪些人获得了两个奖项,来知道自己有多眼红。

输入格式

第一行两个整数 n,m,表示有 n 个人获得科技创新奖,m 个人获得特殊贡献奖。

第二行 n 个正整数,表示获得科技创新奖的人的编号。

第三行 m 个正整数,表示获得特殊贡献奖的人的编号。

输出格式

输出一行,为获得两个奖项的人的编号,按在科技创新奖获奖名单中的先后次序输出。

复制代码
输入
4 3
2 15 6 8
8 9 2
输出
2 8
说明/提示
对于 60% 的数据,0≤n,m≤1000,获得奖项的人的编号 <2×10^9;
对于 100% 的数据,0≤n,m≤10^5,获得奖项的人的编号 <2×10^9;
输入数据保证第二行任意两个数不同,第三行任意两个数不同。

下述代码90分。我觉得毫无问题。

复制代码
#include<stdio.h>
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	int i,j;
	long long a[100000];//特别注意说明提示里的数据范围,如果用int提交是不对的
	for(i=0;i<n;i++){
		scanf("%lld",&a[i]);//输入获科技奖的编号
	}
	long long b[100000];//同上
	for(i=0;i<m;i++){
		scanf("%lld",&b[i]);//输入获贡献奖的编号
	}
	int sign=1;//用于标志有两个奖都获得的编号
	for(i=0;i<n;i++){
		for(j=0;j<m;j++){
			if(a[i]==b[j]){
				sign=1;
				break; //如果找到两个奖都获得的编号,则就结束上层循环,因为后面不可能再有相等的了
			}else{
				sign=0;//如果找不到相等的则就标志为0
			}
		}
		if(sign){
			printf("%lld ",a[i]);
		}else{
			continue;
		} 
	} 
	return 0;
} 

下面代码正确

复制代码
#include <stdio.h>
#include <stdlib.h>
int cmp(const void *a, const void *b) {//这是一个回调函数,这一行代码即为比较函数的原型。
    return *(int *)a - *(int *)b;
}
int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    int a[n], b[m];  
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    for (int i = 0; i < m; i++) scanf("%d", &b[i]);
    qsort(b, m, sizeof(int), cmp);
    int first = 1;//标志是否是第一个出现的元素
    for (int i = 0; i < n; i++) {
        if (bsearch(&a[i], b, m, sizeof(int), cmp)) {
            if (!first) printf(" ");//如果不是第一个就输出空格
            printf("%d", a[i]);
            first = 0;//当输出一个元素后,设置为first=0,表示已经不是第一个了
        }
    }
    return 0;
}
  1. #include <stdlib.h>,此头文件包含qsort快速排序函数和bsearch二分查找函数。

int cmp(const void *a, const void *b);

· 这是一个回调函数,用于 qsort() 和 bsearch() 的比较

· a 和 b 是要比较的两个元素的指针(void* 类型)

· 返回值为:

· 负数:a 在 b 之前(a < b)

· 零:a 等于 b

· 正数:a 在 b 之后(a > b)

return *(int *)a - *(int *)b;

步骤分解:

  1. (int *)a:将 void* 指针强制转换为 int* 指针

· void* 是通用指针,不知道指向什么类型

· 但是现在我们需要比较整数,所以转为 int*

  1. *(int *)a:解引用转换后的指针,获取实际的整数值

· 取 a 指针指向的整数值

  1. *(int *)a - *(int *)b:计算两个整数的差值

· 如果 a 的值 < b 的值 → 返回负数

· 如果 a 的值 = b 的值 → 返回 0

· 如果 a 的值 > b 的值 → 返回正数

2.qsort(b, m, sizeof(int), cmp);

函数原型void qsort(void *base, size_t nmemb, size_t size,

int (*compar)(const void *, const void *));用于快速排序。

其中的参数详解:b是数组名,实际上是指向数组第一个元素的指针;虽然是int*类型,但会自动转换为void*,成为qsort()想要的。这是要排序的数组。

m是数组b的元素个数,类型是size_t(无符号整数),用于告诉qsort()函数有多少个元素需要排序

sizeof(int)每个元素的大小,因为qsort()函数不知道元素类型,需要知道移动多少个字节来访问下一个元素

cmp比较函数,是之前定义的函数指针,qsort()会多次调用这个函数来比较两个元素。

// qsort 内部大致这样使用你的 cmp 函数:

void qsort_impl(void *base, size_t n, size_t size,

int (*cmp)(const void *, const void *)) {

// 当需要比较两个元素时:

char *elem1 = (char *)base//数组初始位置 + i * size;//数组元素大小; // 第 i 个元素的位置

char *elem2 = (char *)base + j * size; // 第 j 个元素的位置

int result = cmp(elem1, elem2); // 调用你的比较函数

// 根据 result 的正负决定是否交换元素

}

具体实例:

int b[] = {30, 10, 50, 20, 40};

int m = 5;

qsort(b, m, sizeof(int), cmp);

// 排序过程:

// 1. qsort 取 b[0] (30) 和 b[1] (10)

// 2. 调用 cmp(&b[0], &b[1]) → 比较 30 和 10

// 3. cmp 返回 30 - 10 = 20(正数),表示 30 > 10

// 4. qsort 知道需要交换它们的位置

// 5. 最终 b 变为 [10, 20, 30, 40, 50]

3.二分查找函数

bsearch(&a[i], b, m, sizeof(int), cmp)

函数原型

void *bsearch(const void *key, const void *base,

size_t nmemb, size_t size,

int (*compar)(const void *, const void *));

找到则返回指向该元素的指针,否则返回NULL;用于比较要查找的关键值和数组中的元素

&a[i]要查找的关键值,虽然是int*类型,但会自动转换为void*,这是我们要在数组b中查找的值

b是已排序好的数组,二分查找只能在有序数组上工作。

m是数组b的元素个数,告诉bsearch要查找范围的大小。

sizeof(int)每个元素的大小,因为qsort()函数不知道元素类型,需要知道移动多少个字节来访问下一个元素。

cmp必须和排序时使用的比较函数完全相同。

二分查找的工作原理

假设 b = [10, 20, 30, 40, 50],查找 a[i] = 30:

// bsearch 内部过程:

  1. low = 0, high = m-1 = 4

  2. mid = (0+4)/2 = 2

  3. 比较 b[2] (30) 和 key (30):

cmp(&30, &30) → 返回 0 → 找到!

  1. 返回 &b[2](指向 30 的指针)

// 如果查找 a[i] = 25:

  1. low=0, high=4, mid=2

  2. b[2]=30, cmp(&25, &30) = -5 → 向左查找

  3. low=0, high=1, mid=0

  4. b[0]=10, cmp(&25, &10) = 15 → 向右查找

  5. low=1, high=1, mid=1

  6. b[1]=20, cmp(&25, &20) = 5 → 向右查找

  7. low=2, high=1 → 查找失败,返回 NULL

i=0: a[0]=3 → 不在 b 中 → 跳过

i=1: a[1]=42 → 在 b 中 →

first=1 → 不输出空格 → 输出"42" → first=0

当前输出:"42"

i=2: a[2]=7 → 不在 b 中 → 跳过

i=3: a[3]=13 → 在 b 中 →

first=0 → 输出空格 → 输出" 13" → first=0

当前输出:"42 13"

i=4: a[4]=8 → 不在 b 中 → 跳过

最终输出:"42 13"