261. Graph Valid Tree
題目給定一個數字 n
和一個陣列,n
代表的是一個圖中,所用有的節點數量,陣列的每個項目表達的是哪兩個節點相連結,題目要問的是這個圖究竟是不是一棵樹?
在回答這個問題之前,需要先確定幾件事情,確認這些事情是很重要的,因為這會影響題目如何做決策。
這個圖要成為一棵樹的條件如下
- 這個圖不會任意有節點形成封閉的環 (Ring) ,而當這個圖形沒有環的時候,其實可以把任意一個點當作樹的根節點,剩下的節點就可以變成子樹了。
- 題目並沒有提到,但是在面試時需要想到的是,題目給定的
n
,是不是所有的節點都有相連?這個意思就是,有一種情況是,這個圖並沒有環出現,但是其圖形是兩個樹,在這個題目中這樣是不行的。
這個題目透過圖形的深度優先搜索,其實要找出是否有環並不困難,這就很像是 141. Linked List Cycle ,這個題目就是當站在某一個節點,並往他相鄰的所有節點不斷的探索,如果發現造訪過同樣的一個節點,代表就有環出現了。
在 Linked List 的資料結構中,要找出環很方便是因為 Linked List 具有一定的方向性,但是這個題目給定的是哪兩個節點互相連接,會變得很難確定出方向,所以題目給定的表達式,我自己會想要先轉換成一個比較方便的形式來處理。
我自己選擇的是把這個表達式改成用 Hash Table 的方式來儲存,這樣的好處是什麼?當查到某個節點時,可以知道該節點有哪些子節點,而當造訪子節點時,可以夠過 Hash Table 的方式來進而去遍歷該子樹,這恰恰也是這個題目所問,如果可以遍歷所有的子樹跟節點,那就是一個完整的樹,如果在遍歷的過程中,發現重複造訪相同的子樹,就代表環存在於此圖形中。
第二個要思考的問題是,想要知道方向,但是題目給定的條件其實我們並不知道方向,我在這裡也卡滿久的,那就是在建立這個 Hash Table 的時候,究竟是第一個數字我們要當作 key 然後第二個數字當作子樹的節點,還是第二個數字當作 key ,然後把第一個數字當作子樹的節點?
class Solution:
def validTree(self, n: int, edges: List[List[int]]) -> bool:
graph = defaultdict(list)
for edge in edges:
n1, n2 = edge
graph[n1].append(n2)
graph[n2].append(n1)
seen = set()
def dfs(node, parent):
if node in seen:
return None
seen.add(node)
for adj in graph[node]:
if adj == parent:
continue
if adj in seen:
return False
res = dfs(adj, node)
if not res:
return False
return True
return dfs(0, -1) and len(seen) == n