题解:学而思编程 唯一最小数
【题目来源】
学而思编程:唯一最小数
【题目描述】
给定一个长度为 \(n\) 的整数数组 \(a_1,a_2,\dots ,a_n\)。
请你找到数组中只出现过一次的数当中最小的那个数。
输出找到的数的索引编号。
\(a_1\) 的索引编号为 \(1\),\(a_2\) 的索引编号为 \(2\),…,\(a_n\) 的索引编号为 \(n\)。
【输入】
第一行包含整数 \(T\),表示共有 \(T\) 组测试数据。
每组数据第一行包含整数 \(n\)。
第二行包含 \(n\) 个整数 \(a_1,a_2,\dots ,a_n\)。
【输出】
每组数据输出一行结果,即满足条件的数的索引编号,如果不存在满足条件的数,则输出 \(−1\)。
【输入样例】
2
2
1 1
3
2 1 3
【输出样例】
-1
2
【核心思想】
-
问题分析:给定长度为 \(n\) 的数组 \(a_1, a_2, \ldots, a_n\),找出只出现过一次的数中最小的那个数的索引。这是一个频次统计 + 线性扫描问题,关键在于先统计每个数字的出现次数,再遍历找满足条件的最小值。
-
算法选择:
- 频次统计:用数组
cnt记录每个数字出现的次数 - 线性扫描找最小:遍历数组,在出现次数为 \(1\) 的数字中找最小值,记录其索引
- 频次统计:用数组
-
关键步骤:
- 初始化:读取 \(T\)(测试组数)
- 处理每组数据:
- 读取 \(n\) 和数组 \(a[1..n]\)
memset(cnt, 0, sizeof(cnt)):重置计数数组- 统计频次:遍历 \(i\) 从 \(1\) 到 \(n\):
cnt[a[i]]++ - 寻找唯一最小数:
ans = -1- 遍历 \(i\) 从 \(1\) 到 \(n\):
- 若
cnt[a[i]] == 1(只出现一次):- 若
ans == -1或a[ans] > a[i]:ans = i(更新为更小值的索引)
- 若
- 若
- 输出
ans
-
时间/空间复杂度:
- 时间复杂度:\(O(T \times n)\),每组数据两次线性遍历
- 空间复杂度:\(O(V)\),\(V\) 为值域大小,计数数组
cnt
-
频次统计的核心思想:
- 两次扫描策略:第一次扫描统计频次,第二次扫描找目标值。分离统计与查询,避免边统计边判断时的信息不完整
- 最小值维护:
ans初始为 \(-1\) 表示未找到,找到第一个唯一值后,后续只需比较数值大小更新索引 - 值域限制:题目隐含值域不大(
cnt数组直接开 \(2 \times 10^5 + 10\)),可用数组计数;若值域大可用map - 索引优先:题目要求输出索引而非数值,因此
ans存储索引,比较时用a[ans]取对应数值 - 适用于"频次约束下的极值查找"类问题,频次统计将重复判断优化为 \(O(1)\) 查询
【算法标签】
模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int a[N], cnt[N];
int main()
{
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
memset(cnt, 0, sizeof(cnt)); // 重置计数数组
for (int i = 1; i <= n; i++)
{
cin >> a[i];
cnt[a[i]]++; // 统计每个数字出现的次数
}
int ans = -1;
for (int i = 1; i <= n; i++)
{
// 寻找只出现一次的数字
if (cnt[a[i]] == 1)
{
// 更新答案为最小值的索引
if (ans == -1 || a[ans] > a[i])
{
ans = i;
}
}
}
cout << ans << endl; // 输出索引,如果不存在则输出-1
}
return 0;
}
【运行结果】
2
2
1 1
-1
3
2 1 3
2
浙公网安备 33010602011771号