LeetCode 133. 克隆圖 | Python

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:

示例 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:

示例 2
輸入:adjList = [[]]
輸出:[[]]
解釋:輸入包含一個空列表。該圖僅僅只有一個值為 1 的節(jié)點,它沒有任何鄰居。

示例 3:

輸入:adjList = []
輸出:[]
解釋:這個圖是空的,它不含任何節(jié)點。

示例 4:

示例 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é)點。那么我們在進行訪問搜索的時候,完成圖的深拷貝。

深度優(yōu)先搜索(DFS)

這里先說下需要注意的地方,因為題目中明確說了,圖是無向圖,圖中的邊是無向邊。例如示例 4:

示例 4

這里節(jié)點 1 和節(jié)點 2 存在無向邊,也就是說節(jié)點 1 可以到節(jié)點 2,而節(jié)點 2 也可以到 節(jié)點 1。所以我們遍歷搜索的時候要注意標(biāo)記,否則的話容易陷入死循環(huá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é)果


實現(xiàn)結(jié)果 | 深度優(yōu)先搜索
實現(xiàn)結(jié)果 | 廣度優(yōu)先搜索

歡迎關(guān)注


公眾號 【書所集錄

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容