题目描述
在一款多人游戏中,每局比赛需要多个玩家参与。如果发现两名玩家至少一起玩过两局比赛,则可以认为这两名玩家互为队友。现在你有一份玩家(通过玩家ID标识)和比赛局次(通过比赛ID标识)的历史记录表,目标是帮助某位指定玩家找到所有符合条件的队友。
例如,已知以下比赛历史记录:
题目解析
1. 分析题目需求:
每名玩家用玩家ID标识,每局比赛用比赛ID标识。
历史记录表包含玩家ID和比赛ID的关联信息。
对于指定玩家 ididid,需要找出所有至少一起玩过两局比赛的玩家ID。
2. 算法思路:
分组匹配:
首先找到目标玩家 ididid 所参加的比赛ID集合。
遍历历史记录表,记录每名玩家参与的比赛ID集合。
统计交集:
对每名玩家,计算其比赛集合与目标玩家比赛集合的交集大小。
如果交集大小大于等于2,则该玩家为目标玩家的队友。
3. 数据结构:
使用 Map<Integer, Set> 保存每个玩家参与的比赛ID集合。
使用 Set 便于快速计算比赛集合的交集。
代码实现(JAVA):
import java.util.*;
public class Main {
public static void main(String[] args) {
// 测试用例1
System.out.println(
Arrays.equals(
solution(1, 10,
new int[][] {
{ 1, 1 }, { 1, 2 }, { 1, 3 }, { 2, 1 }, { 2, 4 }, { 3, 2 },
{ 4, 1 }, { 4, 2 }, { 5, 2 }, { 5, 3 }
}),
new int[] { 4, 5 }));
// 测试用例2
System.out.println(
Arrays.equals(
solution(2, 6,
new int[][] {
{ 2, 1 }, { 2, 3 }, { 1, 1 }, { 1, 2 }, { 3, 1 }, { 4, 3 }
}),
new int[] {}));
// 测试用例3
System.out.println(
Arrays.equals(
solution(3, 8,
new int[][] {
{ 3, 1 }, { 3, 2 }, { 3, 3 }, { 4, 1 }, { 5, 2 }, { 6, 3 },
{ 7, 1 }, { 7, 2 }
}),
new int[] { 7 }));
}
public static int[] solution(int id, int num, int[][] array) {
// 1. 创建玩家ID到比赛ID集合的映射
Map<Integer, Set<Integer>> playerGames = new HashMap<>();
for (int[] record : array) {
int playerId = record[0];
int gameId = record[1];
playerGames.putIfAbsent(playerId, new HashSet<>());
playerGames.get(playerId).add(gameId);
}
// 2. 获取目标玩家的比赛ID集合
Set<Integer> targetGames = playerGames.getOrDefault(id, Collections.emptySet());
if (targetGames.isEmpty()) {
return new int[] {}; // 如果目标玩家没有记录,直接返回空
}
// 3. 找到所有符合条件的队友
List<Integer> teammates = new ArrayList<>();
for (Map.Entry<Integer, Set<Integer>> entry : playerGames.entrySet()) {
int playerId = entry.getKey();
Set<Integer> games = entry.getValue();
// 跳过目标玩家自己
if (playerId == id) {
continue;
}
// 计算交集大小
Set<Integer> intersection = new HashSet<>(games);
intersection.retainAll(targetGames);
if (intersection.size() >= 2) {
teammates.add(playerId);
}
}
// 4. 返回队友列表,按玩家ID排序
Collections.sort(teammates);
return teammates.stream().mapToInt(Integer::intValue).toArray();
}
}
代码详解
1. 玩家比赛映射:
遍历输入数组 array,将每个玩家的比赛记录存入 Map<Integer, Set> 中。
2. 目标玩家比赛集合:
从映射中获取目标玩家 ididid 的比赛集合 targetGames。
若目标玩家无比赛记录,直接返回空结果。
3. 交集计算:
对于每名玩家,计算其比赛集合与 targetGames 的交集大小。
使用 retainAll 方法简化交集计算。
4. 结果处理:
将满足条件的玩家ID加入列表 teammates。
对结果列表排序,并转换为数组返回。
个人思考与分析
1. 难点解析:
交集计算: 如何高效计算玩家比赛集合的交集是本题的核心问题。通过 HashSet 的 retainAll 方法,可以轻松实现交集计算。
性能优化: 为了减少不必要的计算,通过映射预处理玩家数据,将遍历与交集计算的复杂度降低。
2. 算法适用场景:
本题的方法可以推广到类似社交网络中的好友关系计算问题,如“共同好友”或“共同兴趣”。
3. 个人收获:
通过本题目,熟悉了使用 HashSet 和 Map 进行复杂关系映射和交集运算对解决“交集型”问题的思路有了更深刻的理解