You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从双败淘汰赛败者组赛事数组构建递归结构对象?

双败淘汰赛败者组递归对阵树生成方案

解决思路

败者组的对阵结构是从决赛倒推构建的核心逻辑:每个轮次的胜者,都是由上一轮两场比赛的胜者对决产生;而首轮败者组的参赛者是胜者组首轮的败者,没有前置败者组比赛。我们可以通过「赛事数据预处理 + 递归倒推」的方式生成目标结构:

  1. 先整理赛事数据,建立轮次与比赛的映射、队伍获胜记录与对应比赛的映射,方便快速查询。
  2. 从败者组决赛(最高轮次的比赛)的胜者出发,递归向下拆解每个队伍的晋级路径——每个节点的子节点对应其晋级前的对决对手,对手的路径则继续递归处理。

具体实现(JavaScript)

function buildLowerBracketTree(games) {
    // 预处理1:按轮次分组存储比赛
    const roundGames = {};
    // 预处理2:记录「轮次-队伍-获胜比赛」的映射
    const winnerToGame = {};

    games.forEach(game => {
        // 按轮次归类比赛
        if (!roundGames[game.round]) {
            roundGames[game.round] = [];
        }
        roundGames[game.round].push(game);

        // 确定当前比赛的胜者,并记录对应关系
        const winner = game.home_score > game.visitor_score ? game.home_name : game.visitor_name;
        if (!winnerToGame[game.round]) {
            winnerToGame[game.round] = {};
        }
        winnerToGame[game.round][winner] = game;
    });

    // 找到败者组决赛的轮次(最高轮次)
    const maxRound = Math.max(...Object.keys(roundGames).map(Number));
    const finalGame = roundGames[maxRound][0];
    const champion = finalGame.home_score > finalGame.visitor_score ? finalGame.home_name : finalGame.visitor_name;

    // 递归构建单个节点的函数
    function buildNode(team, currentRound) {
        const game = winnerToGame[currentRound][team];
        const node = { name: team };

        // 处理当前比赛的两个对手,生成子节点
        node.children = [game.home_name, game.visitor_name].map(opponent => {
            const prevRound = currentRound - 1;
            // 如果对手在上一轮的败者组有获胜记录,递归构建其路径
            if (winnerToGame[prevRound] && winnerToGame[prevRound][opponent]) {
                return buildNode(opponent, prevRound);
            }
            // 否则对手是直接进入当前轮次的(胜者组淘汰/败者组首轮参赛者),生成基础节点
            return { name: opponent };
        });

        return node;
    }

    // 从败者组冠军开始构建整棵树
    return buildNode(champion, maxRound);
}

// 测试示例数据
const games = [
  { id: 1, home_name: "Team 1", visitor_name: "Team 3", home_score: 1, visitor_score: 0, round: 2 },
  { id: 2, home_name: "Team 6", visitor_name: "Team 7", home_score: 1, visitor_score: 0, round: 2 },
  { id: 3, home_name: "Team 1", visitor_name: "Team 6", home_score: 0, visitor_score: 1, round: 3 },
  { id: 4, home_name: "Team 4", visitor_name: "Team 5", home_score: 1, visitor_score: 0, round: 3 },
  { id: 5, home_name: "Team 6", visitor_name: "Team 4", home_score: 1, visitor_score: 0, round: 4 },
];

console.log(JSON.stringify(buildLowerBracketTree(games), null, 2));

代码说明

  • 数据预处理:通过两次遍历,把比赛按轮次归类,同时记录每个队伍在对应轮次的获胜比赛,为后续递归查询提供快速入口。
  • 递归逻辑:从败者组冠军出发,每一层节点对应该队伍在某轮的获胜比赛;子节点则是这场比赛的两个对手——如果对手是从败者组上一轮晋级的,就继续递归拆解其路径;如果是直接进入当前轮次的(比如胜者组淘汰的队伍),则生成无嵌套的基础节点。
  • 适配性:只要赛事数组的round字段正确反映败者组的轮次顺序,无论参赛队伍数量多少,该逻辑都能自动适配生成对应的递归结构。

内容的提问来源于stack exchange,提问作者RomanOks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 01:05:03