49.红包运气排行榜<字节青训营-简单题>

43 阅读7分钟

1.题目

问题描述

小C参与了一场抢红包的游戏,现在他想要对所有参与抢红包的人进行一次运气排名。排名规则如下:抢到的金额越多,排名越靠前;如果两个人抢到的金额相同,则按照他们抢红包的顺序进行排名。比如,如果小C和小U抢到的金额相同,但小C比小U先抢,则小C排在小U前面。


测试样例

样例1:

输入:n = 4 ,s = ["a", "b", "c", "d"] ,x = [1, 2, 2, 1]

输出:['b', 'c', 'a', 'd']

样例2:

输入:n = 3 ,s = ["x", "y", "z"] ,x = [100, 200, 200]

输出:['y', 'z', 'x']

样例3:

输入:n = 5 ,s = ["m", "n", "o", "p", "q"] ,x = [50, 50, 30, 30, 20]

输出:['m', 'n', 'o', 'p', 'q']

2.思路

一个人可以多次抢红包,需要对这个人抢红包的金额累加后再比较,如果金额相同,则按第一次抢到红包的时间先后排序。

定义两个unordered_map startcnt ,start 存储了字符串作为键(key),索引作为值(value)。键是字符串,值是该字符串在输入列表 s 中第一次出现的索引(位置),cnt 存储了字符串作为键(key),对应的整数值(value)是该字符串所有出现位置上 x[i] 数值的总和。

定义vector a,存放cnt中出现的所有名字,按照cnt值降序排序,若cnt值相同,则按照start索引值升序排序,对a排序,最后return a,即为排序后的结果。

3.代码

最初的理解

最初不知道一个人可以多次抢红包,所以按一个人只出现一次去写逻辑,不能通过所有测试样例。

#include <iostream>
#include <utility>
#include <vector>
#include <string>
#include <map>
#include <algorithm>

using namespace std;

vector<string> solution(int n, vector<string> s, vector<int> x) {
    // write code here
    map<string, int>  nameToAmount;
    for (int i = 0; i < n; i++){
        nameToAmount[s[i]] = x[i];
    }
    // 将map中的元素转移到vector中
    vector<pair<string, int>> nameAmountPairs(nameToAmount.begin(), nameToAmount.end());
    
    // 对vector进行排序,先按红包金额降序排序,红包金额相同的按名字再原map中的顺序排序
    sort(nameAmountPairs.begin(), nameAmountPairs.end(), [](const pair<string, int>& a, const pair<string, int>& b){
        if (a.second != b.second) {
            return a.second > b.second;
        }
        return false;
    });

    //提取排序后的名字
    vector<string> result;
    for (const auto& pair : nameAmountPairs) {
        result.push_back(pair.first);
    }
    return result;
}

int main() {
    cout << (solution(4, {"a", "b", "c", "d"}, {1, 2, 2, 1}) == vector<string>{"b", "c", "a", "d"}) << endl;
    cout << (solution(3, {"x", "y", "z"}, {100, 200, 200}) == vector<string>{"y", "z", "x"}) << endl;
    cout << (solution(5, {"m", "n", "o", "p", "q"}, {50, 50, 30, 30, 20}) == vector<string>{"m", "n", "o", "p", "q"}) << endl;
    return 0;
}

代码详解:

vector<pair<string, int>> nameAmountPairs(nameToAmount.begin(), nameToAmount.end());

  • vector<pair<string, int>> : 定义了一个 vector,其中每个元素是一个 pair<string, int> ,表示名字和对应的红包金额。
  • nameToAmount.begin() : 返回 nameToAmount 这个 map 的起始迭代器。
  • nameToAmount.end() : 返回 nameToAmount 这个 map 的结束迭代器。
  • vector<pair<string, int>> nameAmountPairs(nameToAmount.begin(), nameToAmount.end()); : 使用 map 的迭代器范围构造一个 vector,将 map 中的所有元素复制到 vector 中。

第四个样例有误

输入
n= 12
S = ["aa","aaaaaaa","aaaa","aaaa","aaaa","aaaaaaaaaa","aaaaaaaaa","aaaa","aaaaaaaaaa","aaaaaaaaa","aaaaa","aaaa"] 
X = [17,14,11,2,8,16,14,17,10,6,5,12] 
输出
你的输出 = ["aa","aaaaaaa","aaaa","aaaaaaaaaa","aaaaaaaaa","aaaaa"] 
预期输出 = ["aaaa","aaaaaaaaaa","aaaaaaaaa","aa","aaaaaaa","aaaaa"] 

这道题题目不是特别清楚,实际上是可以一个人多次抢红包,需要对这个人抢红包的金额累加后再比较。 我的代码中遇到一个人多次抢红包时,将这个人的信息更新到了最后一次出现的情况。具体来说,假设有一个人在多个时刻(多次)参与了抢红包的操作,每次出现时,会更新这个人对应的数据,但最终保留的是他在最后一次出现时的状态,而之前的状态被覆盖。

修改逻辑

输入答案后生成的Python代码如下,可以提交通过

def solution(n: int, s: list, x: list) -> list:
    assert n == len(s) == len(x)
    start = {}
    cnt = {}
    for i in range(n):
        if s[i] not in start.keys():
            start[s[i]] = i
        cnt[s[i]] = cnt.get(s[i], 0) + x[i]
    a = sorted(cnt.keys(), key=lambda s: (-cnt[s], start[s]))
    return a

if __name__ == '__main__':
    print(solution(n = 4 ,s = ["a", "b", "c", "d"] ,x = [1, 2, 2, 1]) == ['b', 'c', 'a', 'd'])
    print(solution(n = 3 ,s = ["x", "y", "z"] ,x = [100, 200, 200]) == ['y', 'z', 'x'])
    print(solution(n = 5 ,s = ["m", "n", "o", "p", "q"] ,x = [50, 50, 30, 30, 20]) == ['m', 'n', 'o', 'p', 'q'])

image.png 但用AI转成cpp它会换逻辑,还是不能通过(😵)

image.png

#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
using namespace std;

vector<string> solution(int n, vector<string> s, vector<int> x) {
    // 将名字和红包金额存放到map中
    map<string, int> nameToAmount;
    for (int i = 0; i < n; i++) {
        nameToAmount[s[i]] = x[i];
    }

    // 将map中的元素转移到vector中
    vector<pair<string, int>> nameAmountPairs(nameToAmount.begin(), nameToAmount.end());

    // 对vector进行排序,先按红包金额降序排序,红包金额相同的按名字在原map中的顺序排序
    sort(nameAmountPairs.begin(), nameAmountPairs.end(), [](const pair<string, int>& a, const pair<string, int>& b) {
        if (a.second != b.second) {
            return a.second > b.second;  // 按红包金额降序排序
        }
        return a.first < b.first;  // 红包金额相同的按名字在原map中的顺序排序
    });

    // 提取排序后的名字
    vector<string> result;
    for (const auto& pair : nameAmountPairs) {
        result.push_back(pair.first);
    }

    return result;
}

int main() {
    cout << (solution(4, {"a", "b", "c", "d"}, {1, 2, 2, 1}) == vector<string>{"b", "c", "a", "d"}) << endl;
    cout << (solution(3, {"x", "y", "z"}, {100, 200, 200}) == vector<string>{"y", "z", "x"}) << endl;
    cout << (solution(5, {"m", "n", "o", "p", "q"}, {50, 50, 30, 30, 20}) == vector<string>{"m", "n", "o", "p", "q"}) << endl;
    return 0;
}

image.png 用ChatGPT重新转了c++,可以通过:

#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>

using namespace std;

vector<string> solution(int n, vector<string>& s, vector<int>& x) {
    unordered_map<string, int> start;
    unordered_map<string, int> cnt;

    for (int i = 0; i < n; ++i) {
        if (start.find(s[i]) == start.end()) {
            start[s[i]] = i;  // 记录每个字符串第一次出现的索引
        }
        cnt[s[i]] += x[i];  // 累加每个字符串对应的 x 值
    }

    // 按照 cnt 值降序排序,若 cnt 值相同,则按照 start 索引升序排序
    vector<string> a;
    for (const auto& pair : cnt) {
        a.push_back(pair.first);
    }

    sort(a.begin(), a.end(), [&cnt, &start](const string& s1, const string& s2) {
        if (cnt[s1] != cnt[s2]) {
            return cnt[s1] > cnt[s2];  // 按照 cnt 值降序排序
        }
        return start[s1] < start[s2];  // 如果 cnt 值相同,则按 start 索引升序排序
    });

    return a;
}

int main() {
    // 测试案例
    vector<string> s1 = {"a", "b", "c", "d"};
    vector<int> x1 = {1, 2, 2, 1};
    vector<string> result1 = solution(4, s1, x1);
    for (const auto& str : result1) cout << str << " ";  // 输出: b c a d
    cout << endl;

    vector<string> s2 = {"x", "y", "z"};
    vector<int> x2 = {100, 200, 200};
    vector<string> result2 = solution(3, s2, x2);
    for (const auto& str : result2) cout << str << " ";  // 输出: y z x
    cout << endl;

    vector<string> s3 = {"m", "n", "o", "p", "q"};
    vector<int> x3 = {50, 50, 30, 30, 20};
    vector<string> result3 = solution(5, s3, x3);
    for (const auto& str : result3) cout << str << " ";  // 输出: m n o p q
    cout << endl;

    return 0;
}

代码详解:

unordered_map 中的元素是 无序的,即它们并不按照插入的顺序或者键的大小顺序排列,而是通过哈希值来组织存储的。

if (start.find(s[i]) == start.end())

  • 如果找到键 s[i]find 返回指向该元素的迭代器。
  • 如果找不到,find 返回指向容器尾部的迭代器,即 start.end()
  • start.find(s[i]) == start.end() 的条件是 s[i] 不在 start,也就是第一次遇到 s[i] 这个字符串。
sort(a.begin(), a.end(), [&cnt, &start](const string& s1, const string& s2){
        if (cnt[s1] != cnt[s2]) {
            return cnt[s1] > cnt[s2];
        }
        return start[s1] < start[s2];
    });
  1. sort(a.begin(), a.end(), ...)

排序使用了第三个参数:一个自定义的排序函数(这里是一个 lambda 表达式),这个函数定义了排序的规则。

  1. Lambda 表达式 [&cnt, &start](const string& s1, const string& s2)

这是一个lambda 表达式,即一个匿名的函数对象。它接受两个参数 s1s2,它们是待比较的字符串。s1s2 对应于排序中正在比较的两个元素。

[&cnt, &start]:这里使用了 捕获列表,意思是:

  • &cnt&start 表示捕获 cntstart 两个外部变量的引用,供 lambda 表达式内使用。
  1. if (cnt[s1] != cnt[s2])

这个条件判断用来比较 cnts1s2 对应的值:

  • cnt[s1] 表示 s1 对应的总和(数值)。
  • cnt[s2] 表示 s2 对应的总和(数值)。

如果 cnt[s1]cnt[s2] 不相同,则根据它们的值进行排序:

  • return cnt[s1] > cnt[s2];:如果 cnt[s1] 大于 cnt[s2],则返回 true,表示 s1 应该排在 s2 前面(降序排序)。
  1. return start[s1] < start[s2];

如果 cnt[s1] == cnt[s2](即 s1s2 对应的值相同),则比较它们在 start 中的索引。

  • start[s1] 表示字符串 s1 第一次出现的索引。
  • start[s2] 表示字符串 s2 第一次出现的索引。

start[s1] < start[s2] 表示如果 s1 的第一次出现位置较早(即索引较小),则 s1 应该排在 s2 前面(升序排序)。

整体排序逻辑:

首先,根据 cnt[s1]cnt[s2] 的大小进行排序,降序排列(cnt[s1] > cnt[s2])。

如果 cnt 值相同,则使用 start[s1]start[s2] 的索引进行排序,升序排列(start[s1] < start[s2])。