[LeetCode] #133. Clone Graph

題目

Given a reference of a node in a connected undirected graph.

Return a deep copy (clone) of the graph.

Each node in the graph contains a value (int) and a list (List[Node]) of its neighbors.

直覺解

構想:

用 dictionary 儲存已經複製好的節點,因為節點的 val 和節點的編號是一樣的,所以就用 val 的值當作 key。用 waiting_stack 儲存待複製的節點。

while 迴圈:從 waiting_stack 裡拿出節點,先複製這個節點。然後迭代該節點的鄰居,把鄰居複製一份,把複製的鄰居 append 到 neighbors 裡;因為鄰居的鄰居還沒有被複製,所以需要把鄰居放進去 waiting_stack。

python 3 實作:

def cloneGraph(self, node: 'Node') -> 'Node': 

    if not node:
        return None

    def clone_node(node):
        if not clonedNodes.get(node.val):
            clonedNodes[node.val] = Node(node.val)
        return clonedNodes[node.val]

    clonedNodes = {}
    waiting_stack = [node]
    while waiting_stack:
        current = waiting_stack.pop()
        clone_node(current)
        for neighbor in current.neighbors:
            clone = clone_node(neighbor)
            if clone not in clonedNodes[current.val].neighbors:
                clonedNodes[current.val].neighbors.append(clone)

            if not clone.neighbors and neighbor not in waiting_stack:
                waiting_stack.append(neighbor)

    return clonedNodes[1]

效能:

第一次

Runtime: 32 ms, faster than 93.99% of Python3 online submissions for Clone Graph.
Memory Usage: 14.6 MB, less than 81.03% of Python3 online submissions for Clone Graph.

第二次

Runtime: 40 ms, faster than 55.88% of Python3 online submissions for Clone Graph.
Memory Usage: 14.6 MB, less than 81.07% of Python3 online submissions for Clone Graph.

第三次

Runtime: 40 ms, faster than 55.88% of Python3 online submissions for Clone Graph.
Memory Usage: 14.6 MB, less than 55.80% of Python3 online submissions for Clone Graph.

犯過的錯誤

  • 一開始設定成,凡是在 clonedNodes 裡出現過的節點,就不能放進 waiting_stack 裡。只被複製了 val,但 neighbors 還沒複製的節點,就沒被放進 waiting_stack 裡,導致有些節點的 neighbors 是 []。於是LeetCode 就一直提醒我「You must return a copy of all the nodes in the original graph」,我花了滿多時間才弄懂為什麼會出現這個提示。
  • 把相同的節點重複放進 waiting_stack 裡,導致該節點的鄰居被重複 append。例如,本來鄰居是 [1, 2],因為該節點在 waiting_stack 裡出現兩次,複製後,鄰居變成 [1, 2, 1, 2]
  • 一度搞混了原始的節點和複製的節點。腦袋要再清醒一點,程式碼易讀性也需要再加強。

參考別人的解

來源:Fast, clear DFS python

構想:

用遞迴取代 waiting_stack

自己的 python 3 實作:

def cloneGraph(self, node: 'Node') -> 'Node': 

    def clone_node(node):
        if not node:
            return None

        if cloned_nodes_dict.get(node.val):
            return cloned_nodes_dict[node.val]

        copy = Node(node.val)
        cloned_nodes_dict[node.val] = copy
        for neighbor in node.neighbors:
            copy.neighbors.append(clone_node(neighbor))

        return copy

    cloned_nodes_dict = {}
    return clone_node(node)

效能:

Runtime: 28 ms, faster than 98.83% of Python3 online submissions for Clone Graph.
Memory Usage: 14.6 MB, less than 55.80% of Python3 online submissions for Clone Graph.

研究別人的解

來源:Python BFS

研究感想:

乍看之下,覺得跟我的直覺解構想差不多,因為從 waiting_stack 裡面拿東西的方向不一樣,所以我是 DFS 而他的是 BFS。但是,為什麼他的程式碼那麼精簡,我的看起來就那麼厚重?

後來發現,我跟這個人寫法的差別,在於

  • 每次的 while 迴圈,我都會複製一次 current 節點,但別人沒有這麼做。因為其他所有節點都可以用鄰居的關係連接到,所以真的不必像我那樣,每次都複製一次 current 節點。

  • 1. 複製節點的鄰居時,我還需要多檢查鄰居有沒有重複。

    2. 把節點放進 waiting_stack 前,我還需要多檢查該節點是否已經在 waiting_stack 裡面。

    但別人只是巧妙地使用了 if nei not in clones: 這個判斷條件,就避免了上述兩個檢查在解決的問題。我因為把 if nei not in clones 的檢查封裝在另一個函式裡,所以沒辦法這樣做。

優化我的 python 3 實作:

def cloneGraph(self, node: 'Node') -> 'Node': 

    if not node:
        return None

    cloned = {}
    cloned[node.val] = Node(node.val)
    waiting_stack = [node]

    while waiting_stack:
        curr = waiting_stack.pop()
        for neighbor in curr.neighbors:
            if neighbor.val not in cloned:
                cloned[neighbor.val] = Node(neighbor.val)
                waiting_stack.append(neighbor)
            cloned[curr.val].neighbors.append(cloned[neighbor.val])

    return cloned[node.val]

效能:

第一次

Runtime: 36 ms, faster than 79.75% of Python3 online submissions for Clone Graph.
Memory Usage: 14.8 MB, less than 24.90% of Python3 online submissions for Clone Graph.

第二次

Runtime: 40 ms, faster than 55.88% of Python3 online submissions for Clone Graph.
Memory Usage: 14.8 MB, less than 24.90% of Python3 online submissions for Clone Graph.

第三次

Runtime: 40 ms, faster than 55.88% of Python3 online submissions for Clone Graph.
Memory Usage: 14.6 MB, less than 55.80% of Python3 online submissions for Clone Graph.

第四次

Runtime: 40 ms, faster than 55.88% of Python3 online submissions for Clone Graph.
Memory Usage: 14.5 MB, less than 93.54% of Python3 online submissions for Clone Graph.

第五次

Runtime: 40 ms, faster than 55.88% of Python3 online submissions for Clone Graph.
Memory Usage: 14.6 MB, less than 55.80% of Python3 online submissions for Clone Graph.

Comments

Popular posts from this blog

Alpha Camp 全端開發課程學習心得

在 javascript 用 regular expression 為金額加上千位數分隔符號

shop_platform - sqlalchemy.exc.TimeoutError