问题描述
小M想要通过查看往届游戏比赛的排名来确定自己比赛的目标分数。他希望找到往届比赛中排名第三的分数,作为自己的目标。具体规则如下:
- 如果分数中有三个或以上不同的分数,返回其中第三大的分数。
- 如果不同的分数只有两个或更少,那么小M将选择最大的分数作为他的目标。
请你帮小M根据给定的分数数组计算目标分数。
测试样例
样例1:
输入:
n = 3,nums = [3, 2, 1]
输出:1
样例2:
输入:
n = 2,nums = [1, 2]
输出:2
样例3:
输入:
n = 4,nums = [2, 2, 3, 1]
输出:1
解题思路
小M需要确定目标分数,可以将问题分为以下步骤:
- 去重:因为只有不同的分数才会被考虑,所以需要将数组中的重复分数去掉。
- 排序:对去重后的分数进行降序排序,这样可以方便地找到第3大的分数。
- 判断分数数量:
- 如果有3个或更多不同分数,则返回排序后第3大的分数。
- 如果不足3个不同分数,则返回最大的分数。
算法实现
使用 Python 实现算法:
unique_scores = list(set(nums))
# 按降序排序
unique_scores.sort(reverse=True)
# 返回结果
if len(unique_scores) >= 3:
return unique_scores[2]
else:
return unique_scores[0]
使用C++实现:
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
int solution(int n, std::vector<int> nums) {
std::set<int> uniqueScores(nums.begin(), nums.end());
std::vector<int> sortedScores(uniqueScores.begin(), uniqueScores.end());
std::sort(sortedScores.begin(), sortedScores.end(), greater<int>());
if (sortedScores.size() >= 3) {
return sortedScores[2];
} else {
return sortedScores[0];
}
}
int main() {
std::cout << (solution(3, {3, 2, 1}) == 1) << std::endl;
std::cout << (solution(2, {1, 2}) == 2) << std::endl;
std::cout << (solution(4, {2, 2, 3, 1}) == 1) << std::endl;
return 0;
}
时间复杂度分析
-
去重:
- 插入到
std::set的时间复杂度是 O(nlogn)。
- 插入到
-
排序:
- 对
std::vector进行排序的时间复杂度是O(mlogm),其中 m 是去重后数组的长度。
- 对
-
总复杂度:
-
O(nlogn),因为m <= n。
优化实现
要实现更低的时间复杂度,我们可以避免完全排序,通过维护一个固定大小为 3 的集合来记录最大的三个不同分数。这种方法可以将时间复杂度降低到**O(n)。**以下是优化后的实现:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int solution(int n, std::vector<int> nums) {
int first = INT_MIN, second = INT_MIN, third = INT_MIN;
for (int num : nums) {
if (num == first || num == second || num == third) {
continue;
}
if (num > first) {
third = second;
second = first;
first = num;
} else if (num > second) {
third = second;
second = num;
} else if (num > third) {
third = num;
}
}
return (third == INT_MIN) ? first : third;
}
int main() {
std::cout << (solution(3, {3, 2, 1}) == 1) << std::endl;
std::cout << (solution(2, {1, 2}) == 2) << std::endl;
std::cout << (solution(4, {2, 2, 3, 1}) == 1) << std::endl;
std::cout << (solution(5, {5, 5, 5, 5, 5}) == 5) << std::endl;
return 0;
}
代码说明
-
维护三个变量:
first、second和third分别记录当前的最大值、第二大值和第三大值。- 初始值设置为
INT_MIN,表示尚未找到对应的分数。
-
遍历数组更新最大值:
- 每次遍历当前分数:
- 如果大于
first,更新first,并将原来的first和second值依次后移。 - 如果在
first和second之间,更新second和third。 - 如果在
second和third之间,仅更新third。
- 如果大于
- 每次遍历当前分数:
-
跳过重复值:
- 避免重复分数影响更新,直接
continue。
- 避免重复分数影响更新,直接
-
返回结果:
- 如果第三大的分数
third未被更新(仍为INT_MIN),说明不同分数少于 3 个,返回first。
- 如果第三大的分数
时间复杂度分析
-
遍历数组:
- 每个元素只遍历一次,时间复杂度为O(n)。
-
跳过重复值:
- 用常量时间O(1) 判断是否重复。
总复杂度:O(n)。
相较于基于 std::set 的实现,这种方法避免了去重和排序操作,直接在线性时间内完成计算,适合处理大量数据的场景。