如何实现锦标赛多组对阵生成函数?保证每组队伍仅参赛一次
实现思路与代码示例
你的需求本质是单循环赛制的轮次编排——把所有两两对阵分成若干轮,每轮中每个队伍仅参与一场比赛,且所有轮次覆盖全部可能的对阵。下面给两种可行的实现方案:
方案一:固定轮次编排法(高效推荐)
这是体育赛事中常用的单循环编排方式,适合偶数队伍的场景,不需要依赖getMeetingsPossibilities,直接生成轮次,效率最高。
核心逻辑:
- 固定一支队伍(比如编号0),剩余队伍按顺时针/逆时针轮换位置,每轮生成一组配对:
- 把队伍列表分成前后两半,第一轮让前半部分和后半部分对应位置配对
- 除固定队伍外,其他队伍轮换一个位置,重复配对直到生成所有轮次(m支队伍最多有m-1轮)
代码实现:
function getSetsOfMeetings(teamCount) { if (teamCount % 2 !== 0) { throw new Error("仅支持偶数队伍数,奇数队伍需处理轮空逻辑"); } const rounds = []; const teams = Array.from({ length: teamCount }, (_, i) => i); // 固定第一个队伍,剩余队伍用于轮换 const fixedTeam = teams[0]; let rotatingTeams = teams.slice(1); for (let i = 0; i < teamCount - 1; i++) { const currentRound = []; // 固定队伍和轮换组的第一个配对 currentRound.push(`${fixedTeam}-${rotatingTeams[0]}`); // 剩余队伍两两配对(前后对应) for (let j = 1; j < rotatingTeams.length / 2; j++) { const left = rotatingTeams[j]; const right = rotatingTeams[rotatingTeams.length - j]; currentRound.push(`${left}-${right}`); } rounds.push(currentRound); // 轮换:把最后一个元素移到最前面(顺时针轮换) rotatingTeams = [rotatingTeams.pop(), ...rotatingTeams.slice(0, -1)]; } return rounds; } // 测试4支队伍 console.log(getSetsOfMeetings(4)); // 输出:[ ['0-1','2-3'], ['0-3','1-2'], ['0-2','3-1'] ]
方案二:回溯法(适合理解逻辑)
如果一定要基于getMeetingsPossibilities的结果来生成轮次,可以用回溯法:每轮构建时,跟踪已使用的队伍,确保每个队伍只选一次,完成一轮后从候选对阵中移除已用的对阵,再构建下一轮。
代码实现:
// 假设已有getMeetingsPossibilities函数 function getMeetingsPossibilities(teamCount) { const matches = []; for (let i = 0; i < teamCount; i++) { for (let j = i + 1; j < teamCount; j++) { matches.push(`${i}-${j}`); } } return matches; } function getSetsOfMeetings(teamCount) { const allMatches = [...getMeetingsPossibilities(teamCount)]; const rounds = []; const totalRounds = teamCount - 1; while (rounds.length < totalRounds) { const currentRound = []; const usedTeams = new Set(); // 遍历剩余候选对阵,挑选不重复队伍的配对 for (let i = 0; i < allMatches.length; ) { const match = allMatches[i]; const [teamA, teamB] = match.split('-').map(Number); if (!usedTeams.has(teamA) && !usedTeams.has(teamB)) { currentRound.push(match); usedTeams.add(teamA); usedTeams.add(teamB); // 从候选中移除已用对阵 allMatches.splice(i, 1); } else { i++; } } rounds.push(currentRound); } return rounds; } // 测试4支队伍 console.log(getSetsOfMeetings(4)); // 输出:[ ['0-1','2-3'], ['0-2','1-3'], ['0-3','1-2'] ]
关键注意点
- 若队伍数为奇数,需要额外处理轮空逻辑(每轮让一支队伍轮空)
- 方案一的效率远高于方案二,尤其是队伍数量较多时,推荐使用方案一
内容的提问来源于stack exchange,提问作者Johan Testas
相关产品推荐
相关产品推荐

