前言
如果你玩过多人在线游戏,一定会遇到这样的场景:某局游戏里你和几位玩家默契配合,之后你想知道这些人是否是你的“长期队友”。在算法的世界里,这样的关系判断问题可以通过巧妙的设计高效解决!今天,我们结合一道豆包MarsCode的趣味题,探索如何找出一位玩家的所有“长期队友”。
问题背景与挑战
游戏的参与记录是由玩家ID和游戏ID构成的二维数据集。如果两个玩家在至少两局游戏中同时出现,他们就被认为是彼此的队友。问题在于:
- 海量数据:玩家和游戏的数量可能高达数百万级。
- 高效计算:如何快速找到某玩家的所有队友?
- 结果排序:输出结果需要按升序排列。
让我们用一个实际的案例来说明:
解题思路解析
为了高效解决这个问题,我们需要以下步骤:
-
构建玩家-游戏映射:用一个哈希表将每个玩家和他参与过的游戏进行关联。
-
查找目标玩家的游戏集合:确定指定玩家玩过的所有游戏。
-
遍历其他玩家,计算游戏交集:
- 如果某玩家与目标玩家的游戏交集数量大于等于2,则判定为队友。
-
结果排序:将队友ID按升序排序,输出结果。
算法核心:集合操作与交集计算
我们用一个 HashMap 存储玩家与游戏的对应关系,借助集合的交集操作快速判断是否满足队友条件。这种方法充分利用了集合操作的效率,避免了暴力解法的低效。
以下是完整的代码实现:
Java代码实现
import java.util.*;
public class Main {
public static void main(String[] args) {
// 测试输入
Scanner scanner = new Scanner(System.in);
// 读取目标玩家ID
int id = Integer.parseInt(scanner.nextLine());
// 读取记录数量
int num = Integer.parseInt(scanner.nextLine());
// 读取玩家与游戏记录
int[][] records = new int[num][2];
for (int i = 0; i < num; i++) {
String[] parts = scanner.nextLine().split(" ");
records[i][0] = Integer.parseInt(parts[0]);
records[i][1] = Integer.parseInt(parts[1]);
}
// 计算队友
int[] result = solution(id, num, records);
// 输出队友列表
System.out.println(Arrays.toString(result).replaceAll("[\[\],]", ""));
}
public static int[] solution(int id, int num, int[][] array) {
// 玩家游戏记录的 Map
Map<Integer, Set<Integer>> playerGamesMap = new HashMap<>();
// 构建玩家游戏记录
for (int[] record : array) {
int playerId = record[0];
int gameId = record[1];
playerGamesMap.putIfAbsent(playerId, new HashSet<>());
playerGamesMap.get(playerId).add(gameId);
}
// 获取指定玩家参与的游戏集合
Set<Integer> targetPlayerGames = playerGamesMap.getOrDefault(id, new HashSet<>());
// 结果集合
List<Integer> teammates = new ArrayList<>();
// 遍历其他玩家
for (Map.Entry<Integer, Set<Integer>> entry : playerGamesMap.entrySet()) {
int otherPlayerId = entry.getKey();
Set<Integer> otherPlayerGames = entry.getValue();
// 跳过目标玩家自身
if (otherPlayerId == id) continue;
// 计算交集
Set<Integer> intersection = new HashSet<>(targetPlayerGames);
intersection.retainAll(otherPlayerGames);
// 判断交集数量是否 >= 2
if (intersection.size() >= 2) {
teammates.add(otherPlayerId);
}
}
// 对结果进行升序排序
Collections.sort(teammates);
// 转为数组返回
return teammates.stream().mapToInt(Integer::intValue).toArray();
}
}
代码解析
构建玩家-游戏映射
通过遍历记录,将每个玩家与他参与的游戏存入 HashMap 中。
例如,记录 1 1 会将玩家 1 加入 Map,并关联到游戏 1。
for (int[] record : array) {
int playerId = record[0];
int gameId = record[1];
playerGamesMap.putIfAbsent(playerId, new HashSet<>());
playerGamesMap.get(playerId).add(gameId);
}
查找队友逻辑
对每位玩家,与目标玩家的游戏集合求交集,判断交集的大小是否大于等于2。如果满足条件,则将该玩家加入队友列表:
Set<Integer> intersection = new HashSet<>(targetPlayerGames);
intersection.retainAll(otherPlayerGames);
if (intersection.size() >= 2) {
teammates.add(otherPlayerId);
}
排序输出
队友列表需要按升序输出,因此在将结果添加至列表后,用 Collections.sort() 排序。
总结
通过豆包MarsCode平台的实战,我们学会了如何用集合操作解决实际问题,感受到了算法的优雅与高效。如果你正在寻找既有趣又实用的算法题目,这个平台会是一个非常好的选择。
思考延伸:
- 如果要求找到参与同一游戏最多的玩家组合,你会如何设计算法?
- 在分布式环境中,如何优化大规模数据的交集计算?
期待在豆包MarsCode的学习路上和大家继续交流与成长!