1. 程式人生 > 實用技巧 >LeetCode 133. 克隆圖 | Python

LeetCode 133. 克隆圖 | Python

133. 克隆圖


題目來源:力扣(LeetCode)
https://leetcode-cn.com/problems/clone-graph

題目


給你無向 連通 圖中一個節點的引用,請你返回該圖的 深拷貝(克隆)。

圖中的每個節點都包含它的值 val(int) 和其鄰居的列表(list[Node])。

class Node {
    public int val;
    public List<Node> neighbo rs;
}

測試用例格式:

簡單起見,每個節點的值都和它的索引相同。例如,第一個節點值為 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 = []
輸出:[]
解釋:這個圖是空的,它不含任何節點。

示例 4:

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

提示:

  • 節點數不超過 100 。
  • 每個節點值 Node.val 都是唯一的,1 <= Node.val <= 100
  • 無向圖是一個簡單圖,這意味著圖中沒有重複的邊,也沒有自環。
  • 由於圖是無向的,如果節點 p 是節點 q 的鄰居,那麼節點 q 也必須是節點 p 的鄰居。
  • 圖是連通圖,你可以從給定節點訪問到所有節點。

解題思路


思路:DFS、BFS

在這道題中,題目要求的是圖的深拷貝。題目所述的圖的深拷貝,其實就是要構建與原圖結構,值均一樣的圖,但是 其中的節點不能再是原圖的引用。

題目開頭說了,給定的是一個節點的引用。但後面的提示也提及,圖是連通的,可以從給定的節點中去訪問所有節點。那麼我們在進行訪問搜尋的時候,完成圖的深拷貝。

深度優先搜尋(DFS)

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

這裡節點 1 和節點 2 存在無向邊,也就是說節點 1 可以到節點 2,而節點 2 也可以到 節點 1。所以我們遍歷搜尋的時候要注意標記,否則的話容易陷入死迴圈。

下面是具體演算法:

  • 前面說在遍歷訪問的時候進行標記,這裡我們藉助雜湊表來已經被訪問和克隆的節點。其中鍵表示的是原圖的節點,而值表示的是克隆圖中對應的節點;
  • 從給定的節點開始向下搜尋,如果節點存在於雜湊表中,那麼直接返回雜湊表中的對應的節點;
  • 如果節點並沒有被標記,那麼建立克隆節點,儲存到雜湊表中;
  • 遞迴呼叫每個節點的鄰接點,將結果放到克隆鄰接點列表中。

具體的程式碼見【程式碼實現 # 深度優先搜尋】

廣度優先搜尋(BFS)

使用廣度優先搜尋,這裡同樣需要注意無向邊的問題。在這裡,同樣使用雜湊表來儲存已被訪問原圖的節點以及對應克隆節點。下面是具體的演算法:

  • 使用雜湊表來儲存已被訪問原圖的節點以及對應克隆節點;
  • 克隆給定的節點,儲存到雜湊表中。同時藉助輔助佇列,先將給定的節點放到佇列。
  • 出隊,訪問該節點的所有鄰接點。如果節點不在雜湊表中,那麼克隆當前節點的鄰接點存入雜湊表中。同時將此鄰接點入隊,並將此鄰接點放到克隆圖中對應節點的鄰接表中。
  • 重複直至佇列為空,表明圖遍歷結束。

具體的程式碼見【程式碼實現 # 廣度優先搜尋】

程式碼實現


# 深度優先搜尋
"""
# 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]
            
            # 不存在雜湊表中,那麼克隆節點,將其放入雜湊表中
            clone_node = Node(node.val, [])
            marked[node] = clone_node
            # 遍歷節點的鄰接點,相鄰接點放到鄰接列表中
            for neighbor in node.neighbors:
                clone_node.neighbors.append(dfs(neighbor))
            
            return clone_node
        
        return dfs(node)

# 廣度優先搜尋
"""
# 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
            # 克隆節點,放到雜湊表中
            clone_node = Node(node.val, [])
            marked[node] = clone_node
            # 先將給定的節點入隊
            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)
                    # 更新當前節點的鄰接列表,注意是克隆節點
                    marked[cur_node].neighbors.append(marked[neighbor])
            return clone_node
        
        return bfs(node)

實現結果


歡迎關注


公眾號 【書所集錄