圖形結構:克隆圖,圖的遍歷的應用,遞迴和迭代
阿新 • • 發佈:2018-12-31
克隆一張無向圖,圖中的每個節點包含一個 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)