难度:中等
题目
给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。
图中的每个节点都包含它的值 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,它有两个邻居:节点 2 和 4 。
节点 2 的值是 2,它有两个邻居:节点 1 和 3 。
节点 3 的值是 3,它有两个邻居:节点 2 和 4 。
节点 4 的值是 4,它有两个邻居:节点 1 和 3 。
示例 2:
输入:adjList = [[]]
输出:[[]]
解释:输入包含一个空列表。该图仅仅只有一个值为 1 的节点,它没有任何邻居。
示例 3:
输入:adjList = []
输出:[]
解释:这个图是空的,它不含任何节点。
提示:
-
这张图中的节点数在
[0, 100]
之间。 -
1 <= Node.val <= 100
-
每个节点值
Node.val
都是唯一的, -
图中没有重复的边,也没有自环。
-
图是连通图,你可以从给定节点访问到所有节点。
解题思路:
使用深度优先搜索(DFS)来解决图的克隆问题。其主要思路是递归地遍历图中的每个节点,并在遍历过程中创建节点的克隆版本。为了避免重复克隆相同的节点,使用了一个 Map
结构 visited
来存储已经克隆过的节点,以节点的引用为键,克隆后的节点为值。
-
初始化:创建一个
Map
叫做visited
,用于存储已经克隆过的节点。如果图中存在循环,或者有多个节点指向同一个节点,这个Map
可以防止无限循环和重复克隆。 -
定义 DFS 函数:
dfs(originalNode)
函数负责克隆originalNode
及其所有邻居节点。 -
检查是否已克隆:在
dfs
函数内部,首先检查originalNode
是否已经在visited
中。如果是,直接返回对应的克隆节点,避免重复工作。 -
创建克隆节点:如果
originalNode
尚未被克隆,创建一个新的Node
实例,将其值设置为originalNode.val
,并将邻居列表初始化为空数组。 -
存储克隆节点:将
originalNode
和其克隆版本的映射存储在visited
中。 -
克隆邻居:遍历
originalNode
的所有邻居,对每个邻居调用dfs
函数来获取其克隆版本,并将这些克隆版本添加到当前克隆节点的邻居列表中。 -
返回克隆节点:最后,
dfs
函数返回当前克隆节点的引用。 -
启动克隆过程:在
cloneGraph
函数外部,调用dfs(node)
来开始克隆过程。node
是要克隆的图的起始节点。
关键点:
- 使用
Map
来跟踪已经克隆的节点,防止重复克隆和无限循环。 - 深度优先搜索(DFS)递归地访问图中的每个节点,确保每个节点及其邻居都被克隆。
- 每个克隆节点的邻居列表是在克隆过程中动态构建的,确保了克隆图的结构与原图完全一致。
这种方法的时间复杂度为 O(V+E),其中 V 是节点数,E 是边数。这是因为每个节点和每条边都会被访问一次。空间复杂度也为 O(V+E),主要是由于递归调用栈和 visited
Map 的使用。
JavaScript实现:
/**
* // Definition for a _Node.
* function _Node(val, neighbors) {
* this.val = val === undefined ? 0 : val;
* this.neighbors = neighbors === undefined ? [] : neighbors;
* };
*/
/**
* Clone an undirected graph using DFS.
* @param {Node} node - The starting node of the graph to be cloned.
* @return {Node} - The cloned starting node of the new graph.
*/
var cloneGraph = function(node) {
if (node === null) return null;
const visited = new Map();
function dfs(originalNode) {
if (visited.has(originalNode)) {
return visited.get(originalNode);
}
const cloneNode = new Node(originalNode.val, []);
visited.set(originalNode, cloneNode);
for (const neighbor of originalNode.neighbors) {
cloneNode.neighbors.push(dfs(neighbor));
}
return cloneNode;
}
return dfs(node);
};