一、题目信息
1. 题目描述
查找一个数组的第K小的数,相同数值算同一个排名(如数组 [2,1,3,4,5,2] 的第3小数为3)。
- 时间限制:1000 ms
- 内存限制:256 mb
- 题目来源:北京邮电大学
2. 输入输出格式
| 输入描述 |
输出描述 |
| 多组数据,每组输入: 1. 数组长度 n(1≤n≤1000) 2. n 个整数 3. 目标排名 k |
输出每组数据的第 k 小整数(题目保证 k 合法) |
3. 样例
复制代码
输入:
6
2 1 3 5 2 2
3
输出:
3
二、核心解题思路
1. 整体流程
多组输入
读取n、数组、k
数组升序排序(qsort)
遍历排序数组,去重计数
找到第k个不同数,输出
2. 关键步骤解析
- 排序 :用 C 标准库
qsort 快速排序(时间复杂度 O(n log n)),替代冒泡排序提升效率;
- 去重计数:排序后相同数值连续排列,遍历数组时仅当当前数与前一个数不同,才将排名计数 +1;
- 终止条件:计数等于 k 时,立即输出当前数并结束(减少无效遍历)。
三、完整代码(优化版)
c
复制代码
#include <stdio.h>
#include <stdlib.h>
// qsort比较函数:整型升序(防整数溢出)
int compare_int(const void *a, const void *b) {
int num1 = *(const int *)a;
int num2 = *(const int *)b;
// 三目运算符替代 num1-num2,避免 INT_MAX - (-1) 溢出
return (num1 > num2) ? 1 : (num1 < num2) ? -1 : 0;
}
int main() {
int n, k;
// 多组输入:scanf返回1表示成功读取n,直到输入结束
while (scanf("%d", &n) == 1) {
int arr[1001] = {0}; // n≤1000,固定数组满足内存限制
// 读取n个整数
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
scanf("%d", &k); // 读取k值
// 快速排序:数组首地址、元素数、单个元素大小、比较函数
qsort(arr, n, sizeof(int), compare_int);
// 去重计数找第k小
int count = 1; // 初始排名:第一个数为第1小
int result = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] != arr[i-1]) { // 遇到不同数,排名+1
count++;
result = arr[i];
if (count == k) break; // 找到目标,提前退出
}
}
printf("%d\n", result); // 输出结果
}
return 0;
}
四、核心知识点
1. qsort 函数(重点)
| 参数 |
说明 |
void *base |
待排序数组首地址(void* 适配任意类型) |
size_t nitems |
数组元素个数(sizeof(arr)/sizeof(arr[0]) 计算) |
size_t size |
单个元素字节大小(如 sizeof(int)) |
compar |
比较函数指针,返回值规则: 负数→a在前(升序)、0→相等、正数→a在后(降序) |
2. 防溢出的比较函数
- 错误写法:
return num1 - num2;(可能溢出,如 num1=INT_MAX,num2=-1);
- 正确写法:
return (num1 > num2) ? 1 : (num1 < num2) ? -1 : 0;。
3. 多组输入处理
- 用
while (scanf("%d", &n) == 1) 循环,直到输入结束(控制台输入 Ctrl+Z/文件结束)。
4. 去重计数逻辑
| 排序后数组 |
[1,2,2,2,3,5] |
| 计数过程 |
1(count=1)→ 2(count=2)→ 3(count=3) |
| 结果 |
3(第3小) |
五、易错点总结
- qsort 参数错误 :误将
size 传成数组总字节数(正确应为单个元素字节数);
- 比较函数溢出 :直接用
num1 - num2 导致整数溢出;
- 多组输入遗漏:未用 while 循环处理多组数据;
- 计数逻辑错误 :在
if(count==k) 外未提前 break,导致无效遍历。
六、优化点
- 效率:qsort 比冒泡排序(O(n²))效率更高,n=1000 时优势明显;
- 内存 :固定数组
int arr[1001] 满足题目 n≤1000 的限制,无需动态分配;
- 性能:找到第 k 个值后立即 break,避免遍历整个数组。