1. 程式人生 > >圖形結構:克隆圖,圖的遍歷的應用,遞迴和迭代

圖形結構:克隆圖,圖的遍歷的應用,遞迴和迭代

克隆一張無向圖,圖中的每個節點包含一個 label (標籤)和一個 neighbors (鄰接點)列表 。

克隆圖時圖的遍歷的應用,樹的遍歷,圖的遍歷是最基本的操作,他們和陣列的遍歷是一樣的,線性結構的問題都是在陣列遍歷的基礎上進行操作,同樣樹的問題和圖的問題也都是在其遍歷的基礎上操作,我們所要做的是在遍歷的基礎上新增資料的操作。

# Definition for a undirected graph node
class UndirectedGraphNode:
    def __init__(self, x):
        self.label = x
        self.
neighbors = [] # 克隆圖是圖的遍歷的應用,就是我們在遍歷圖時需要進行一些操作 class Solution: # @param node, a undirected graph node # @return a undirected graph node def cloneGraph(self, node): if not node: return node # visited是一個字典:node.label:新複製的結點,node.label可以定位原來的結點 visited =
{} root = UndirectedGraphNode(node.label) visited[node.label] = root # 實際上是圖的遍歷,所以指標是指向原圖的 stack = [node,] while stack: curNode = stack.pop() for neighborNode in curNode.neighbors: if neighborNode.
label not in visited: # 這個步驟做了2個操作,相當於標記了原圖中這個結點已經訪問了 # 二是複製了這個結點,鄰居還有待新增進去 visited[neighborNode.label] = UndirectedGraphNode(neighborNode.label) # 標記了結點之後,結點入棧 stack.append(neighborNode) # 既然新圖鄰居結點都複製了,那麼就可以更新新圖的鄰居列表了 visited[curNode.label].neighbors.append(visited[neighborNode.label]) return root class Solution2: # @param node, a undirected graph node # @return a undirected graph node def cloneGraph(self, node): if not node: return node #visited是一個字典:原圖的node:新複製的結點,這樣使用更加簡潔,速度更快 visited = {} root = UndirectedGraphNode(node.label) visited[node] = root stack = [node,] while stack: curNode = stack.pop() for neighborNode in curNode.neighbors: if neighborNode not in visited: visited[neighborNode] = UndirectedGraphNode(neighborNode.label) stack.append(neighborNode) visited[curNode].neighbors.append(visited[neighborNode]) return root

=============================================================================

下面來總結一下克隆圖的思路,首先圖形的遍歷很清楚了,我們所要做的是:在遍歷每一個結點

時,複製該結點以及他的鄰接結點,但是有一個問題,這時新圖的鄰接結點還沒有新建,就沒有

辦法,更新新圖這個結點的鄰接結點表,但是我們在遍歷原圖的時候,會把原圖的鄰接結點都

一個一個的放入到棧中或者佇列中,在放入前我們就可以把新圖的結點複製了,這樣新圖結點

鄰接結點都存在了,就可以直接添加了,這裡面需要注意,原圖遍歷的時候結點入棧時會去重

但是新圖需要把所有的鄰接結點都新增進去,只要確認他的列表裡面的結點都建立了,就行了

這就是為什麼visited[curNode].neighbors.append(visited[neighborNode])在if語句的

外面。然後,我們還是沒有講到為什麼會想到使用{原結點:新結點}的字典,這是因為對一個結點

操作有3個,一是新建結點(未更新鄰接表),二是更新自己鄰接表,三是被用作更新其他結點的

鄰接表。我們操作過程一直都是在原圖上遍歷,也就是指標是指向原圖的結點,新圖對應的在哪裡

我們不知道,把原結點和新結點一一對應起來,就相當於一個指標同時指向了原圖和新圖。

類似的可以擴充套件,可以把任意的遍歷看成最簡單的陣列遍歷,就是指標按照一定方式走,假如兩個陣列

可以關聯起來,可以使用一個指標同時遍歷兩個陣列,簡單化遍歷之後,一般的問題都只是新增

遍歷過程的處理。

=============================================================================

遞迴方式,更加能從整體考察問題:

class Solution3:
    # @param node, a undirected graph node
    # @return a undirected graph node
    def cloneGraph(self,node):
        if not node:
            return node
        
        # 原問題初始化
        visited ={}
        root = UndirectedGraphNode(node.label)
        visited[node] = root       
        
        def Rec(node,visited):
            # 處理每一個子問題
            for neighborNode in node.neighbors:
                if neighborNode not in visited:
                    visited[neighborNode] = UndirectedGraphNode(neighborNode.label)
                    # 處理子問題
                    Rec(neighborNode,visited)
                visited[node].neighbors.append(visited[neighborNode])
                
        # 處理原問題
        Rec(node,visited)
        return root

# 還有一種思路:不是從子圖來思考問題,而是從子圖延申到子問題,這個問題是克隆子圖,
# 克隆子圖裡面的每一個結點,並返回克隆的始頂點
class Solution4:
    # @param node, a undirected graph node
    # @return a undirected graph node
    def cloneGraph(self,node):
        if not node:
            return node
        
        # 原問題初始化
        visited ={}       
        
        def cloneGraphRec(node,visited):
            if node in visited:
                return visited[node]
            
            visited[node] = UndirectedGraphNode(node.label)
            # 處理每一個子問題
            for neighborNode in node.neighbors:
                visited[node].neighbors.append(cloneGraphRec(neighborNode,visited))
            
            return visited[node]
                
        # 處理原問題
        return cloneGraphRec(node,visited)