比赛中排名第三的分数

101 阅读5分钟

题目解析:

小M想通过查看往届比赛的排名,找到排名第三的分数作为自己的目标分数。具体规则如下:

  • 如果有三个或以上不同的分数,返回其中第三大的分数。
  • 如果不同的分数只有两个或更少,那么返回最大的分数。

我们的任务是,根据给定的分数数组,计算出小M的目标分数。


思路分析:

为了找到第三大的不同分数,我们需要处理以下两个问题:

  1. 消除重复分数: 因为题目要求的是不同的分数,所以需要去除数组中的重复元素。
  2. 找到第三大的分数: 在去重后的分数集合中,找到第三大的分数。如果不同分数的数量不足三,则返回最大的分数。

算法设计:

为了高效地解决这个问题,我们可以采取以下步骤:

  1. 使用数据结构去重:

    • 集合(Set): 利用集合的性质,可以方便地去除重复元素。
    • 优先队列(堆): 维护一个大小为3的最小堆,存储最大的三个不同分数。
  2. 遍历数组,更新前三大的分数:

    • 使用变量跟踪: 使用三个变量 firstsecondthird,分别存储第一、第二、第三大的不同分数。
    • 比较更新: 遍历数组时,比较当前分数与已存储的前三大分数,进行更新。
    • 避免重复: 在更新过程中,确保不考虑已存在的分数。

代码实现:

下面提供一种 时间复杂度为 O(n) 的解决方案,使用三个变量跟踪前三大的不同分数。

#include <vector>
#include <climits>

int findThirdMax(const std::vector<int>& nums) {
    int64_t first = LLONG_MIN;
    int64_t second = LLONG_MIN;
    int64_t third = LLONG_MIN;

    for (const 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 == LLONG_MIN ? static_cast<int>(first) : static_cast<int>(third);
}

代码解释:

  • 第3行: 包含必要的头文件 vectorclimits,后者用于使用 LLONG_MIN

  • 第5行: 定义函数 findThirdMax,接受一个整数向量 nums

  • 第6-8行: 初始化三个变量 firstsecondthird,分别为最小的 64 位整数 LLONG_MIN,用于存储前三大的不同分数。

  • 第10-17行: 遍历 nums 数组,对于每个元素 num

    • 第11行: 如果 num 已经等于 firstsecondthird,则跳过,避免重复处理相同的分数。
    • 第13-15行: 如果 num 大于当前的 first,则依次更新 firstsecondthird
    • 第15-17行: 否则,如果 num 大于 secondthird,则相应更新。
  • 第19行: 如果 third 仍然是初始值 LLONG_MIN,说明不同的分数不足三个,返回最大的分数 first;否则,返回第三大的分数 third


测试样例验证:

  • 样例1:

    输入:nums = [3, 2, 1]
    输出:1
    
    • 处理后,前三大的分数分别为 first=3second=2third=1
    • 返回 third=1
  • 样例2:

    输入:nums = [1, 2]
    输出:2
    
    • 不同的分数只有两个,first=2second=1
    • 返回 first=2
  • 样例3:

    输入:nums = [2, 2, 3, 1]
    输出:1
    
    • 去重后,分数为 [2, 3, 1]
    • 前三大的分数为 first=3second=2third=1
    • 返回 third=1

知识总结:

  • 变量跟踪法: 在需要找出前几大的元素时,可以使用有限的变量来跟踪,而不需要对整个数组进行排序,提高了效率。
  • 注意边界值: 在初始化变量时,使用了 LLONG_MIN,确保能正确处理数组中包含最小整数的情况。
  • 避免重复: 通过在遍历时跳过已存在的分数,保证了计算的是不同的分数。

学习方法与心得:

  1. 理解题意,明确要求:

    • 在解题前,认真阅读题目,确保理解所有的要求和限制条件。
  2. 选择合适的数据结构和算法:

    • 根据题目的时间和空间复杂度要求,选择最合适的解法。
    • 本题中,通过变量跟踪的方法,避免了排序和额外的空间开销。
  3. 注意特殊情况的处理:

    • 考虑数组中可能存在的重复元素,以及不同分数不足三个的情况。
  4. 代码实现中的细节:

    • 使用 LLONG_MIN 初始化变量,避免了与数组中的最小值冲突。
    • 在比较时,注意先检查是否与已有的最大值重复。

学习建议:

  • 多练习经典问题: 例如寻找数组中的最大值、最小值、前几大元素等,熟悉不同的解法。
  • 强化编程基本功: 熟练掌握编程语言中的数据类型和常量,例如 INT_MINLLONG_MIN 等。
  • 总结常用算法技巧: 例如使用变量跟踪法、哈希表、堆等,在不同的场景下灵活应用。

学习计划:

  1. 每日刷题: 利用豆包MarsCode AI刷题功能,坚持每天解答一道算法题,巩固所学知识。
  2. 错题归纳: 对于做错的题目,记录错误原因,定期复习,加强对薄弱环节的理解。
  3. 深入理解算法: 对于每道题目,不仅要会做,还要理解背后的算法思想和优化方法。
  4. 分享交流: 加入算法学习社区,与他人分享心得体会,互相学习。

工具运用:

  • 豆包MarsCode AI刷题功能: 提供了丰富的题库和即时的反馈,帮助我们高效地练习算法题目。
  • 结合其他资源: 如算法书籍、在线课程和技术博客,拓展知识面,学习不同的解题思路。
  • 实践与应用: 在实际项目中尝试应用所学算法,加深理解,积累经验。

结语:

通过对这道题目的解析和学习,我们掌握了如何在不使用排序和额外空间的情况下,找到数组中第三大的不同元素。这不仅提高了我们的算法设计能力,也培养了我们细致考虑问题的习惯。希望我的学习方法和心得能对大家有所帮助,让我们一起在算法学习的道路上不断进步!