133. 克隆圖
題目來源:力扣(LeetCode)
https://leetcode-cn.com/problems/clone-graph
題目
給你無向 連通 圖中一個節(jié)點的引用,請你返回該圖的 深拷貝(克鲁)秦爆。
圖中的每個節(jié)點都包含它的值 val(int)
和其鄰居的列表(list[Node]
)捏检。
class Node {
public int val;
public List<Node> neighbo rs;
}
測試用例格式:
簡單起見,每個節(jié)點的值都和它的索引相同氢橙。例如氮墨,第一個節(jié)點值為 1(val = 1
),第二個節(jié)點值為 2(val = 2
)算利,以此類推。該圖在測試用例中使用鄰接列表表示寇蚊。
鄰接列表 是用于表示有限圖的無序列表的集合笔时。每個列表都描述了圖中節(jié)點的鄰居集棍好。
給定節(jié)點將始終是圖中的第一個節(jié)點(值為 1)仗岸。你必須將 給定節(jié)點的拷貝 作為對克隆圖的引用返回允耿。
示例 1:
輸入:adjList = [[2,4],[1,3],[2,4],[1,3]]
輸出:[[2,4],[1,3],[2,4],[1,3]]
解釋:
圖中有 4 個節(jié)點。
節(jié)點 1 的值是 1扒怖,它有兩個鄰居:節(jié)點 2 和 4 较锡。
節(jié)點 2 的值是 2,它有兩個鄰居:節(jié)點 1 和 3 盗痒。
節(jié)點 3 的值是 3蚂蕴,它有兩個鄰居:節(jié)點 2 和 4 。
節(jié)點 4 的值是 4俯邓,它有兩個鄰居:節(jié)點 1 和 3 骡楼。
示例 2:
輸入:adjList = [[]]
輸出:[[]]
解釋:輸入包含一個空列表。該圖僅僅只有一個值為 1 的節(jié)點稽鞭,它沒有任何鄰居鸟整。
示例 3:
輸入:adjList = []
輸出:[]
解釋:這個圖是空的,它不含任何節(jié)點朦蕴。
示例 4:
輸入:adjList = [[2],[1]]
輸出:[[2],[1]]
提示:
- 節(jié)點數(shù)不超過 100 篮条。
- 每個節(jié)點值 Node.val 都是唯一的,
1 <= Node.val <= 100
吩抓。 - 無向圖是一個簡單圖涉茧,這意味著圖中沒有重復(fù)的邊,也沒有自環(huán)疹娶。
- 由于圖是無向的伴栓,如果節(jié)點 p 是節(jié)點 q 的鄰居,那么節(jié)點 q 也必須是節(jié)點 p 的鄰居蚓胸。
- 圖是連通圖挣饥,你可以從給定節(jié)點訪問到所有節(jié)點。
解題思路
思路:DFS沛膳、BFS
在這道題中扔枫,題目要求的是圖的深拷貝。題目所述的圖的深拷貝锹安,其實就是要構(gòu)建與原圖結(jié)構(gòu)短荐,值均一樣的圖,但是 其中的節(jié)點不能再是原圖的引用叹哭。
題目開頭說了忍宋,給定的是一個節(jié)點的引用。但后面的提示也提及风罩,圖是連通的糠排,可以從給定的節(jié)點中去訪問所有節(jié)點。那么我們在進(jìn)行訪問搜索的時候超升,完成圖的深拷貝入宦。
深度優(yōu)先搜索(DFS)
這里先說下需要注意的地方哺徊,因為題目中明確說了,圖是無向圖乾闰,圖中的邊是無向邊落追。例如示例 4:
這里節(jié)點 1 和節(jié)點 2 存在無向邊,也就是說節(jié)點 1 可以到節(jié)點 2涯肩,而節(jié)點 2 也可以到 節(jié)點 1轿钠。所以我們遍歷搜索的時候要注意標(biāo)記,否則的話容易陷入死循環(huán)病苗。
下面是具體算法:
- 前面說在遍歷訪問的時候進(jìn)行標(biāo)記疗垛,這里我們借助哈希表來已經(jīng)被訪問和克隆的節(jié)點。其中鍵表示的是原圖的節(jié)點硫朦,而值表示的是克隆圖中對應(yīng)的節(jié)點继谚;
- 從給定的節(jié)點開始向下搜索,如果節(jié)點存在于哈希表中阵幸,那么直接返回哈希表中的對應(yīng)的節(jié)點花履;
- 如果節(jié)點并沒有被標(biāo)記,那么創(chuàng)建克隆節(jié)點挚赊,存儲到哈希表中诡壁;
- 遞歸調(diào)用每個節(jié)點的鄰接點,將結(jié)果放到克隆鄰接點列表中荠割。
具體的代碼見【代碼實現(xiàn) # 深度優(yōu)先搜索】
廣度優(yōu)先搜索(BFS)
使用廣度優(yōu)先搜索妹卿,這里同樣需要注意無向邊的問題。在這里蔑鹦,同樣使用哈希表來存儲已被訪問原圖的節(jié)點以及對應(yīng)克隆節(jié)點夺克。下面是具體的算法:
- 使用哈希表來存儲已被訪問原圖的節(jié)點以及對應(yīng)克隆節(jié)點;
- 克隆給定的節(jié)點嚎朽,存儲到哈希表中铺纽。同時借助輔助隊列,先將給定的節(jié)點放到隊列哟忍。
- 出隊狡门,訪問該節(jié)點的所有鄰接點。如果節(jié)點不在哈希表中锅很,那么克隆當(dāng)前節(jié)點的鄰接點存入哈希表中其馏。同時將此鄰接點入隊,并將此鄰接點放到克隆圖中對應(yīng)節(jié)點的鄰接表中爆安。
- 重復(fù)直至隊列為空叛复,表明圖遍歷結(jié)束。
具體的代碼見【代碼實現(xiàn) # 廣度優(yōu)先搜索】
代碼實現(xiàn)
# 深度優(yōu)先搜索
"""
# Definition for a Node.
class Node:
def __init__(self, val = 0, neighbors = []):
self.val = val
self.neighbors = neighbors
"""
class Solution:
def cloneGraph(self, node: 'Node') -> 'Node':
marked = {}
def dfs(node):
if not node:
return node
# 如果存在于哈希表中,直接返回哈希表存儲的值
if node in marked:
return marked[node]
# 不存在哈希表中褐奥,那么克隆節(jié)點肤寝,將其放入哈希表中
clone_node = Node(node.val, [])
marked[node] = clone_node
# 遍歷節(jié)點的鄰接點,相鄰接點放到鄰接列表中
for neighbor in node.neighbors:
clone_node.neighbors.append(dfs(neighbor))
return clone_node
return dfs(node)
# 廣度優(yōu)先搜索
"""
# Definition for a Node.
class Node:
def __init__(self, val = 0, neighbors = []):
self.val = val
self.neighbors = neighbors
"""
class Solution:
def cloneGraph(self, node: 'Node') -> 'Node':
from collections import deque
marked = {}
def bfs(node):
if not node:
return node
# 克隆節(jié)點抖僵,放到哈希表中
clone_node = Node(node.val, [])
marked[node] = clone_node
# 先將給定的節(jié)點入隊
queue = deque()
queue.append(node)
# 出隊,開始遍歷
while queue:
cur_node = queue.popleft()
for neighbor in cur_node.neighbors:
# 如果鄰接點不在哈希表中缘揪,克隆鄰接點存入哈希表中耍群,并將鄰接點入隊
if neighbor not in marked:
marked[neighbor] = Node(neighbor.val, [])
queue.append(neighbor)
# 更新當(dāng)前節(jié)點的鄰接列表,注意是克隆節(jié)點
marked[cur_node].neighbors.append(marked[neighbor])
return clone_node
return bfs(node)
實現(xiàn)結(jié)果
歡迎關(guān)注
公眾號 【書所集錄】