排名游戏的第三大分数 | 豆包MarsCode AI刷题

55 阅读3分钟

问题描述

小M想要通过查看往届游戏比赛的排名来确定自己比赛的目标分数。他希望找到往届比赛中排名第三的分数,作为自己的目标。具体规则如下:

  1. 如果分数中有三个或以上不同的分数,返回其中第三大的分数。
  2. 如果不同的分数只有两个或更少,那么小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需要确定目标分数,可以将问题分为以下步骤:

  1. 去重:因为只有不同的分数才会被考虑,所以需要将数组中的重复分数去掉。
  2. 排序:对去重后的分数进行降序排序,这样可以方便地找到第3大的分数。
  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;
}

时间复杂度分析

  1. 去重

    • 插入到 std::set 的时间复杂度是 O(nlogn)。
  2. 排序

    • std::vector 进行排序的时间复杂度是O(mlogm),其中 m 是去重后数组的长度。
  3. 总复杂度

  • 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;
}

代码说明

  1. 维护三个变量

    • firstsecondthird 分别记录当前的最大值、第二大值和第三大值。
    • 初始值设置为 INT_MIN,表示尚未找到对应的分数。
  2. 遍历数组更新最大值

    • 每次遍历当前分数:
      • 如果大于 first,更新 first,并将原来的 firstsecond 值依次后移。
      • 如果在 firstsecond 之间,更新 secondthird
      • 如果在 secondthird 之间,仅更新 third
  3. 跳过重复值

    • 避免重复分数影响更新,直接 continue
  4. 返回结果

    • 如果第三大的分数 third 未被更新(仍为 INT_MIN),说明不同分数少于 3 个,返回 first

时间复杂度分析

  1. 遍历数组

    • 每个元素只遍历一次,时间复杂度为O(n)。
  2. 跳过重复值

    • 用常量时间O(1) 判断是否重复。

总复杂度:O(n)。

相较于基于 std::set 的实现,这种方法避免了去重和排序操作,直接在线性时间内完成计算,适合处理大量数据的场景。