问题描述
在一款多人游戏中,每局比赛需要多个玩家参与。如果发现两名玩家至少一起玩过两局比赛,则可以认为这两名玩家互为队友。现在你有一份玩家(通过玩家ID标识)和比赛局次(通过比赛ID标识)的历史记录表,目标是帮助某位指定玩家找到所有符合条件的队友。
例如,已知以下比赛历史记录:
| 玩家ID | 游戏ID |
测试样例
样例1:
输入:
id = 1, num = 10, array = [[1,1], [1,2], [1,3], [2,1], [2,4], [3,2], [4,1], [4,2], [5,2], [5,3]]
输出:[4, 5]
样例2:
输入:
id = 2, num = 6, array = [[2,1], [2,3], [1,1], [1,2], [3,1], [4,3]]
输出:[]
样例3:
输入:
id = 3, num = 8, array = [[3,1], [3,2], [3,3], [4,1], [5,2], [6,3], [7,1], [7,2]]
输出:[7]
题目思路
-
分析关系:
- 游戏和玩家构成了一个双向关系:玩家参与多个游戏,多个玩家也可以参与同一个游戏。
- 需要找出与目标玩家有较高“共同参与度”的玩家,通过统计共同游戏的次数来筛选。
-
算法设计:
- 构建“游戏-玩家”映射,记录每个游戏对应的所有玩家。
- 找出目标玩家参与过的所有游戏。
- 对这些游戏的玩家进行统计,记录每个玩家与目标玩家的共同参与次数。
- 返回共同参与次数不少于2次的玩家列表,并按升序排序。
代码实现
以下C++实现:
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> findTeammates(int id, int num, const vector<vector<int>>& array) {
// 游戏ID -> 玩家集合
unordered_map<int, unordered_set<int>> game_to_players;
// 构建游戏与玩家的映射
for (const auto& record : array) {
int player = record[0];
int game = record[1];
game_to_players[game].insert(player);
}
// 目标玩家参与的游戏ID集合
unordered_set<int> target_games;
for (const auto& record : array) {
if (record[0] == id) {
target_games.insert(record[1]);
}
}
// 玩家出现次数统计(与目标玩家共同参加的次数)
unordered_map<int, int> teammate_count;
for (int game : target_games) {
for (int player : game_to_players[game]) {
if (player != id) {
teammate_count[player]++;
}
}
}
// 找出共同游戏次数 >= 2 的玩家
vector<int> teammates;
for (const auto& [player, count] : teammate_count) {
if (count >= 2) {
teammates.push_back(player);
}
}
// 按升序排序输出结果
sort(teammates.begin(), teammates.end());
return teammates;
}
int main() {
// Add your test cases here
std::vector<int> result = solution(1, 10, {{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 4}, {3, 2}, {4, 1}, {4, 2}, {5, 2}, {5, 3}});
std::cout << (result == std::vector<int>{4, 5}) << std::endl;
return 0;
}
二、知识总结:基于映射与统计的解决思路
新知识点
-
双重映射关系的构建
使用unordered_map实现游戏到玩家集合的映射,解决了“如何快速获取所有参与某游戏的玩家”问题。unordered_map<int, unordered_set<int>> game_to_players; -
统计与筛选结合
通过unordered_map对玩家的共同参与次数进行计数,实现了筛选逻辑:unordered_map<int, int> teammate_count;只需要一次遍历即可记录次数,复杂度为 O(n)O(n)。
-
升序排序结果
输出结果需要按升序排列,使用std::sort进行排序:sort(teammates.begin(), teammates.end());
个人理解与建议
- 这道题突出了对数据结构的理解与灵活使用。初学者应熟练掌握常用容器(如
unordered_map和unordered_set)及其基本操作(插入、查找、遍历)。 - 遇到类似的“关系类”问题时,可以优先考虑图或双向映射关系建模,以快速找到算法的切入点。
三、学习计划:利用刷题提升逻辑思维
高效刷题的策略:
- 明确目标
每日完成1-2道思考性强的题目,尤其是涉及数据结构与算法结合的问题,培养建模能力。 - 逐步深化
从简单题(如哈希表基本操作)入手,逐步拓展到复杂题型(如本题中的映射关系和多条件筛选)。 - 错题记录
对于未通过的测试案例,重点分析原因(如边界条件、遗漏情况),并将优化后的代码与原始实现进行对比。
四、工具运用:结合AI与学习资源
结合MarsCode AI
- 使用MarsCode的题目推荐功能,根据自身水平选择适合的题目类型(如哈希表、图等)。
- 利用其代码分析与优化功能,发现代码中的潜在问题或可优化之处。
其他学习资源
- LeetCode:丰富的题目库是刷题的有力补充。
- 算法书籍:《程序员面试金典》《算法导论》适合作为理论补充,提升算法设计能力。
- 实践项目:将刷题中掌握的知识应用到实际项目中,例如实现一个推荐系统或游戏匹配功能。
五、总结
通过本次题目的解析与代码实现,我们深入理解了如何通过映射与统计结合解决复杂的筛选问题。这种思路不仅在算法刷题中常见,在实际工程应用中同样重要。希望本文能对正在学习算法的同学提供帮助,也期待与更多人交流学习心得,共同进步!