题目描述

给定节点记录 (id,parentId),其中 id 唯一且为正数,parentId=0 表示根节点。

保证所有非零父节点存在,父子关系无环。返回森林的根节点列表,根节点及同级孩子按输入顺序排列。

示例 1:

输入: records = [[2,1],[1,0],[3,0],[4,1]]
输出: [{"id":1,"children":[{"id":2,"children":[]},{"id":4,"children":[]}]},{"id":3,"children":[]}]
解释: 每条记录为 [id,parentId]。1、3 是根,2、4 是 1 的孩子;根及孩子均按输入中的相对顺序排列。

提示:

  • id 为唯一正整数,parentId=0 表示根。
  • 保证非零父节点存在且无环。
  • 根节点及同级孩子按输入顺序排列。

题意分析

父节点可能出现在孩子记录之后,单遍遇到孩子就查找父节点会失败。先创建全部节点,再按关系挂接,可以将对象创建与顺序连接分开,避免重复创建同一节点。

解法:先建 id 映射再按顺序挂接

核心思路

[!blue]

第一遍按唯一 id 创建节点并存入映射,使任何非零父编号在第二遍都能直接找到。映射中的对象就是最终森林里的对象,后续只添加引用,不复制子树。

第二遍严格按照原记录顺序:parentId == 0 的节点追加到根列表,其余追加到父节点的 children。每个节点恰好挂接一次,根和每组同级孩子的相对顺序都由追加顺序保证。

题面已保证父节点存在且无环,因此无需额外搜索或把异常节点当成根。空记录返回空森林;不要改为遍历哈希表挂接,否则无法保证顺序。

解题步骤

  1. 第一遍按每个 id 创建唯一节点,全部放入映射。
  2. 第二遍仍按原输入顺序处理记录,根加入根列表,其他节点加入对应父亲的 children。
  3. 返回根列表,不把孤儿或环偷偷当作额外根。

代码实现

class Node {
    final int id;
    final List<Node> children = new ArrayList<>();

    Node(int id) {
        this.id = id;
    }
}

class Solution {
    public List<Node> forest(int[][] records) {
        Map<Integer, Node> nodes = new HashMap<>();

        for (int[] record : records) {
            nodes.put(record[0], new Node(record[0]));
        }

        List<Node> roots = new ArrayList<>();

        for (int[] record : records) {
            Node node = nodes.get(record[0]);

            if (record[1] == 0) {
                roots.add(node);
            } else {
                nodes.get(record[1]).children.add(node);
            }
        }

        return roots;
    }
}
type Node struct {
    ID       int
    Children []*Node
}

func forest(records [][]int) []*Node {
    nodes := map[int]*Node{}
    for _, record := range records {
        nodes[record[0]] = &Node{ID: record[0]}
    }
    roots := []*Node{}
    for _, record := range records {
        node := nodes[record[0]]
        if record[1] == 0 {
            roots = append(roots, node)
        } else {
            parent := nodes[record[1]]
            parent.Children = append(parent.Children, node)
        }
    }
    return roots
}

复杂度分析

  • 时间复杂度:期望 $O(n)$。
  • 空间复杂度:节点与映射空间 $O(n)$。

关键点总结

[!green]

先建齐节点再连边,使父记录可以晚于孩子出现;根列表及每个父节点的 children 均按输入次序追加。

易错点总结

[!yellow]

本题输入是已校验的森林关系;孤儿节点、重复id和环不是默认根节点,接收外部数据时应先校验这些约束。

相似题目

题目 难度 关联与区别
2196. 根据描述创建二叉树 中等 同样从父子描述创建节点关系,本题允许多个根且用 parentId=0 显式标识根。
133. 克隆图 中等 都需要映射保证每个身份只创建一个节点;本题按 id 挂接,克隆图按原节点对象复用克隆节点。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/18942296
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!