克隆图

67 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第25天,点击查看活动详情

一、题目

leetcode 克隆图

给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。

图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。

class Node {
    public int val;
    public List<Node> neighbors;
}

测试用例格式:

简单起见,每个节点的值都和它的索引相同。例如,第一个节点值为 1(val = 1),第二个节点值为 2(val = 2),以此类推。该图在测试用例中使用邻接列表表示。

邻接列表 是用于表示有限图的无序列表的集合。每个列表都描述了图中节点的邻居集。

给定节点将始终是图中的第一个节点(值为 1)。你必须将 给定节点的拷贝 作为对克隆图的引用返回。

 

示例 1:

输入:adjList = [[2,4],[1,3],[2,4],[1,3]]
输出:[[2,4],[1,3],[2,4],[1,3]]
解释:
图中有 4 个节点。
节点 1 的值是 1,它有两个邻居:节点 24 。
节点 2 的值是 2,它有两个邻居:节点 13 。
节点 3 的值是 3,它有两个邻居:节点 24 。
节点 4 的值是 4,它有两个邻居:节点 13

示例 2:

输入:adjList = [[]]
输出:[[]]
解释:输入包含一个空列表。该图仅仅只有一个值为 1 的节点,它没有任何邻居。

示例 3:

输入:adjList = []
输出:[]
解释:这个图是空的,它不含任何节点。

示例 4:

输入:adjList = [[2],[1]]
输出:[[2],[1]]

提示:*

节点数不超过 100 。
每个节点值 Node.val 都是唯一的,1 <= Node.val <= 100。
无向图是一个简单图,这意味着图中没有重复的边,也没有自环。
由于图是无向的,如果节点 p 是节点 q 的邻居,那么节点 q 也必须是节点 p 的邻居。
图是连通图,你可以从给定节点访问到所有节点。

二、题解

就是说要复制一个联通的链表节点,不是复制节点的引用,而是要完全new出一个节点。

方法一 由于需要复制出一个同样的图节点,所以我们需要遍历该图,遍历过程中构造新图的节点。给定的图为无向联通图,给任意节点都一样,每个节点包含本节点的值,以及其邻居节点的集合。同时每一个节点值与节点的索引相同,例如[1->2->3->4]四个节点,第一个节点为1,其邻居节点为2和4;第二个节点为2,其邻居节点为1和3;第三个节点为3,其邻居节点为2和4;第四个节点为4,其邻居节点为1和3。那么我们可以用DFS深度优先搜索来遍历图节点,以给定的节点开始,如果当前节点为空,就返回空节点,否则构造一个值相同的新节点以及空的邻居集合,然后获取当前节点的邻居节点,最后再以邻居节点开始获取其邻居节点,将邻居节点构造出的节点添加到当前节点的邻居集合中,重复这个过程直到遍历完所有节点。因为需要遍历邻居节点,可以发现某些邻居节点可能之前已经遍历过了,所以需要记录下已经遍历过的节点,如果已经遍历过则不需要新创建节点,直接使用之前创建的。所以我们需要一个节点哈希集合存储已经遍历构造出的新节点,如果遍历的节点存在哈希表中则不需要重新构造。

方法二 同样也可以BFS广度优先搜索来遍历,同时也需要哈希表记录已经遍历的节点,以及一个队列辅助遍历。首先构造出一个同样值的新节点,然后往哈希表中记录下这个节点,然后再将节点加入队列,遍历队列元素节点,然后也遍历其邻居节点,如果其邻居节点没有遍历过(哈希表中不存在)那么就构造出同样的节点,加入哈希表以及队列中,最后将遍历的邻居节点添加到遍历队列节点的邻居集合中。最后遍历完成返回构造的新节点即可。

三、代码

方法一 Java代码

class Solution {
    Map<Node, Node> cloneMap = new HashMap<>();

    public Node cloneGraph(Node node) {
        if (node == null) {
            return null;
        }
        if (cloneMap.containsKey(node)) {
            return cloneMap.get(node);
        }
        Node cloneNode = new Node(node.val, new ArrayList<>());
        cloneMap.put(node, cloneNode);
        for (Node neighbor : node.neighbors) {
            cloneNode.neighbors.add(cloneGraph(neighbor));
        }
        return cloneNode;
    }
}

时间复杂度:O(n),需要遍历图的每一个节点,

空间复杂度:O(n),需要一个哈希表记录以及遍历的节点,以及构造新图节点空间,和递归消耗的栈空间。


方法二 Java代码

class Solution {
    public Node cloneGraph(Node node) {
        if (node == null) {
            return null;
        }
        Node cloneNode = new Node(node.val, new ArrayList<>());
        Map<Node, Node> cloneMap = new HashMap<>();
        cloneMap.put(node, cloneNode);
        Deque<Node> deque = new LinkedList<>();
        deque.offer(node);
        while (!deque.isEmpty()) {
            Node curNode = deque.poll();
            for (Node neighbor : curNode.neighbors) {
                if (!cloneMap.containsKey(neighbor)) {
                    cloneMap.put(neighbor, new Node(neighbor.val, new ArrayList<>()));
                    deque.offer(neighbor);
                }
                cloneMap.get(curNode).neighbors.add(cloneMap.get(neighbor));
            }
        }
        return cloneNode;
    }
}

时间复杂度:O(n),需要遍历图的每一个节点,

空间复杂度:O(n),需要一个哈希表记录以及遍历的节点,以及构造新图节点空间,和一个队列辅助遍历。