LeetCode之路:500. Relative Ranks

一、引言

这道题相对来说比较简单,题目含义也比较容易弄懂。但是这道题容易理解错题意,相信这也是为什么这道题的 Acceptance (通过率)才 47% 的原因。

先来看看题目吧:

Given scores of N athletes, find their relative ranks and the people with the top three highest scores, who will be awarded medals: “Gold Medal”, “Silver Medal” and “Bronze Medal”.

Example 1:

Input: [5, 4, 3, 2, 1]

Output: [“Gold Medal”, “Silver Medal”, “Bronze Medal”, “4”, “5”]

Explanation: The first three athletes got the top three highest scores, so they got “Gold Medal”, “Silver Medal” and “Bronze Medal”. For the left two athletes, you just need to output their relative ranks according to their scores.

Note:
1.N is a positive integer and won’t exceed 10,000.
2.All the scores of athletes are guaranteed to be unique.

题目信息比较少,简单翻译一下:

给定 N 个运动员的成绩,请找出他们的相对排名,并且为成绩最高的三个人授予 “Gold Medal”、”Silver Medal”、”Bronze Medal” (金牌、银牌、铜牌)三种头衔。

注意:
1.N 是一个正整数并且不会超过 10000
2.所有的分数都保证是唯一的

这里有必要大书特书下这个例子:

举例:
输入:[5, 4, 3, 2, 1]
输出:[“Gold Medal”, “Silver Medal”, “Bronze Medal”, “4”, “5”]
解释:前面的 3 个运动员获得了最高的 3 个分数,所以授权了他们 “Gold Medal”、”Silver Medal”、”Bronze Medal” 三种头衔。所以此时还剩下了两个人,我们只需要输出他们的相对排名即可。

这里还是需要解释下,这里的输入是运动员们的成绩的数组,分数越高则排名越高,并且,这里是要输出相对排名。

比如说,这里第一个运动员拿了 5 分,是最高分数,因此我们给了他第一名的头衔,而比如说中间又有一个运动员得了 2 分,是最低分数,那么我们的输出就应该在这个位置输出最低的排名信息。

也就是说:

我们按照输入的顺序依次计算“翻译”各个元素,将对应元素的排名信息返回出来。

明白了上述我分析的逻辑,那么这道题已经被简化成了,如何将指定的数组的各个元素的排名信息写回各个元素的位置上。

二、抓住不变的,建立变化的关系

这里按照我们引言里面的分析,此问题已经变成了我们如何记录指定元素的排名信息的问题了。

那么接下来让我们分析分析,到底如何记录指定元素的排名信息呢?

首先,我们要明确一点,不管怎么变化,元素的值是唯一的,这一点非常非常重要,这意味着我们拿到了一个唯一可以确定该元素信息的“主键”一样的东西。

那么,让我们看看一个元素到底拥有哪些信息:

  1. 元素在原数组中的位置

  2. 元素的值

  3. 元素按从大到小的排名数值

根据分析,我们能够看到给定数组的每一个元素都会拥有以上的 3 个属性。那么其实我们这道题,就是根据“主键”也就是元素的值,记录元素在原数组中的位置,再记录元素的排名数值,最后再按照原数组顺序输出排名数值即可。

那么编写程序的逻辑可以如下:

  1. 保存原数组的顺序

  2. 对数组从大到小进行排序,记录排序信息

  3. 按照原数组的顺序将各个元素的排序数值(前 3 个需要翻译成头衔)输出出来

按照以上的逻辑,代码如下:

// my solution 1 , runtime = 19 ms
class Solution1 {
public:
    vector<string> findRelativeRanks(vector<int>& nums) {
        vector<int> original(nums);
        map<int, int> num_rank;
        vector<string> result;
        sort(nums.begin(), nums.end(), greater<int>());
        for (int i = 0; i < nums.size(); ++i) num_rank[nums[i]] = i;
        for (int j = 0; j < original.size(); ++j) {
            if (num_rank[original[j]] == 0) result.push_back("Gold Medal");
            else if (num_rank[original[j]] == 1) result.push_back("Silver Medal");
            else if (num_rank[original[j]] == 2) result.push_back("Bronze Medal");
            else    result.push_back(to_string(num_rank[original[j]] + 1));
        }
        return result;
    }
};

这里,我使用了一个 vector<int> 类型的数组 original拷贝了参数数组 nums 的一份备份,用来保存原数组的顺序信息;然后对参数数组 nums 进行了从大到小的排序(这里使用了 std::greater<int>()仿函数);最后再遍历 original数组,读出元素的排名信息并且输出到输出数组中。

三、编码的乐趣在于不停地折腾

有没有优化点?
有没有优化点?!
有没有优化点!!!

思考了很久,还是没有想出来,不过想到了一个逻辑上的优化点:

之前使用数组来存储参数数组的顺序,这里也可以使用数组元素值对位置的映射来记录信息,这样的话,代码的可读性更强

于是代码如下:

// my solution 2 use two map , runtime = 29 ms
class Solution2 {
public:
    vector<string> findRelativeRanks(vector<int>& nums) {
        map<int, int> pos;
        map<int, int> rank;
        vector<string> result;
        for (int i = 0; i < nums.size(); ++i) pos[i] = nums[i];
        sort(nums.begin(), nums.end(), greater<int>());
        for (int j = 0; j < nums.size(); ++j) rank[nums[j]] = j;
        for (int z = 0; z < nums.size(); ++z) {
            if (rank[pos[z]] == 0) result.push_back("Gold Medal");
            else if (rank[pos[z]] == 1) result.push_back("Silver Medal");
            else if (rank[pos[z]] == 2) result.push_back("Bronze Medal");
            else result.push_back(to_string(rank[pos[z]] + 1));
        }
        return result;
    }
};

runtime 反而比前一个方法更高了



T_T
编码在于不停地折腾,不要在意这些细节~~~

四、总结

这道题的最高票答案我看了下,大概也都是这种思路,所以这里就没有进行分析了。

这道题还是比较简单的,属于理清楚了逻辑之后,编码解题比较轻松的题目。

其实我们日常的工作中又何尝不是这样的呢?

理清楚各种逻辑,实际上使用到的工具也都是封装好了的:

编程的意义不在于去摸清多么生涩的理论,而是如何灵活运用你所能够掌握的工具去解决实际的逻辑问题。

周五了,尽管明天还要加班 : (

To be Stronger!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值