一魚多吃: 從不同方法確認 兩點之間的路徑存在與否

小松鼠-avatar-img
發佈於演算法題目解析 個房間
更新於 發佈於 閱讀時間約 13 分鐘

今天的一魚三吃系列是透過 兩點之間是否存在一條路徑的題目,來回顧以前學過的DFS、BFS和Disjoint Set,鞏固圖論演算法的知識點。


英文的題目敘述在這裡


題目敘述

給定我們已知n個節點的圖,和圖上的每一條無向邊edges。

請問給定的起點start和終點end是否存在一條路徑可以抵達?


測試範例

Example 1:

raw-image


Input: n = 3, edges = [[0,1],[1,2],[2,0]], source = 0, destination = 2
Output: true
Explanation: There are two paths from vertex 0 to vertex 2:
- 012
- 02


Example 2:

raw-image


Input: n = 6, edges = [[0,1],[0,2],[3,5],[5,4],[4,3]], source = 0, destination = 5
Output: false
Explanation: There is no path from vertex 0 to vertex 5.

約束條件

Constraints:

  • 1 <= n <= 2 * 10^5

節點總數介於 1 ~ 二十萬之間。

  • 0 <= edges.length <= 2 * 10^5

邊的總數介於 0 ~ 二十萬之間。

  • edges[i].length == 2

每個邊由兩個端點所構成。

  • 0 <= ui, vi <= n - 1

節點編號一定是介於 0~n-1 的整數

  • ui != vi

不會有自我指向邊。

  • 0 <= source, destination <= n - 1

起點、終點一定是合法節點編號。

  • There are no duplicate edges.

不會有重複的邊。(也就是說不會有平行邊)

  • There are no self edges.

不會有自我指向邊。


想法

前面在圖論的演算法統整已經學過,看到路徑存在性檢查優先想到DFS,看到等權圖最短路徑想到BFS,看到有權圖的最短路徑想到Dijkstra、Bellman-Ford、Floyd-Warshall。


我們就先從最直覺的DFS出發,接著是等價的BFS寫法。


再來是其他的解法,也可以用集合的觀點來想,每加入一條邊就把相鄰的兩點併入到同一個集合,最後假如起點和終點在同一個集合,代表彼此可以透過圖上的邊來拜訪對方,也就是說,起點到終點至少存在一條路徑可以抵達


DFS演算法 從起點出發,有路就一直往前走

從起點出發,有路就一直往前走,假如可以走到終點,
那代表至少有一條路徑可以抵達終點。


時間複雜度: O(V+E) 每個節點最多拜訪一次,每條邊最多拜訪一次。

空間複雜度: O(V) 每個節點,最多拜訪一次,而且不會重複拜訪

遞回深度最深為O(V)


DFS演算法 遞回版本 程式碼

class Solution:
def validPath(self, n: int, edges: List[List[int]], start: int, end: int) -> bool:

# represent graph by adjacency matrix
graph = defaultdict(list)

# update graph by input edges
for v, u in edges:
graph[v].append(u)
graph[u].append(v)


visited = set()
path = []

# Launch DFS from start node
def dfs( cur ):

visited.add(cur)
path.append( cur )

if cur == end:
#print(path) # uncomment this line if you want to see the path from start to end on debug console
return True

for neighbor in graph[cur]:
if neighbor not in visited and dfs(neighbor):
return True

path.pop( )
return False

# --------------------------------------

return dfs(start)

BFS演算法 從起點出發,同心圓一層一層向外擴散

從起點出發,以起點為同心圓一層一層向外擴散,假如可以碰到終點,
那代表至少有一條路徑可以抵達終點。


時間複雜度: O(V+E) 每個節點最多拜訪一次,每條邊最多拜訪一次。

空間複雜度: O(V) 每個節點,最多拜訪一次,而且不會重複拜訪
Queue長度最深為O(V)


BFS演算法 迭代版本 程式碼

class Solution:
def validPath(self, n: int, edges: List[List[int]], start: int, end: int) -> bool:

# represent graph by adjacency matrix
graph = defaultdict(list)

# update graph by input edges
for v, u in edges:
graph[v].append(u)
graph[u].append(v)


path = []
visited = set()
queue = deque([start])

# Launch BFS from start node
while queue:

cur = queue.popleft()
visited.add(cur)
path.append(cur)

if cur == end:
#print(path) # uncomment this line if you want to see the path from start to end on debug console
return True

for neighbor in graph[cur]:
if neighbor not in visited: queue.append(neighbor)

return False

Disjoint Set 演算法 合併相連的點到同一個集合


集合的觀點來想,每加入一條邊就把相鄰的兩點併入到同一個集合。


最後假如起點和終點在同一個集合,代表彼此可以透過圖上的邊來拜訪對方,也就是說,起點到終點至少存在一條路徑可以抵達


時間複雜度: O(V+E)

O(V) Disjoint Set 初始化的時間成本。

O(E * alpha(V) ) ~ O( E ) Disjoint Set 合併和查詢的成本。

alpha() 是inverse Ackermann function,實務上可視為常數


空間複雜度: O(V) 初始化每個節點各自一個做為一個集合。


Disjoint Set 演算法 迭代版本程式碼

# Disjoin Set Union
class DSU:
def __init__(self, n):
self.parent = { i: i for i in range(n) }
self.rank = { i: 1 for i in range(n) }

def find(self, x):

cur = x
if self.parent[cur] != cur:
self.parent[cur] = self.find( self.parent[cur] )
return self.parent[cur]
else:
return cur

def union(self, x, y):

parent_x, parent_y = self.find(x), self.find(y)
rank_pax, rank_pay = self.rank[parent_x], self.rank[parent_y]

if parent_x != parent_y:
if rank_pax >= rank_pay:
self.parent[parent_y] = parent_x
self.rank[parent_x] = rank_pax + rank_pay
else:
self.parent[parent_x] = parent_y
self.rank[parent_x] = rank_pay + rank_pax
return


class Solution:
def validPath(self, n: int, edges: List[List[int]], start: int, end: int) -> bool:

graph = DSU(n)

# Keep union two nodes which are connected
for node_u, node_v in edges:
graph.union( node_u, node_v)

# If we know start and end node belong to the same set,
# then there is at least one path from start to end
if graph.find(start) == graph.find(end):
return True

# Special case handle
# n == 1 means Graph with one node, in this case, start == end == node_#0
# source node also acts as destination node
return (n == 1) or False

結語

條條大路通羅馬,萬變不離其宗。

一個題目可以有好多種解法,但是背後核心觀念都是類似的

只要掌握基本原理,根據題意,找出目標的明確定義,


接著採用合適的圖論演算法去計算所求(起點到終點的路徑是否存在)即可!


希望能幫助讀者、觀眾徹底理解基本圖論演算法BFS / DFS 的原理,

並且能夠舉一反三,洞察其背後的本質和了解相關的應用領域!


感謝收看囉! 我們下篇文章再見~

avatar-img
90會員
425內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
今天的官方每日一題是Island Perimeter島嶼周長,很有趣的一題。 題目非常直觀好懂。也很適合拿來作為多角度複習、回顧圖論演算法的好題目。 英文的題目敘述在這裡 題目敘述 題目會給我們一個二維陣列當作地圖,格子點為1代表陸地,格子點為0代表海洋。 要求我們以四連通N4的方式拜訪
題目敘述 題目會給定一顆二元樹的根結點, 要求我們在指定的層樹d,插入新的一層,節點值為v。 原本的左、右子樹,就成為新的那一層的左子樹、右子樹。 題目的原文敘述 測試範例 Example 1: Input: root = [4,2,6,3,1,5], val = 1, depth =
這篇文章,會帶著大家複習以前學過的DFS框架, 並且以圖論的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): # 邊界條件 if base case or stop cond
這篇文章,會帶著大家複習以前學過的BFS框架, 並且以圖論的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 BFS 框架 + 演算法 虛擬碼 # Queue 通常初始化成根結點,作為起點 BFS_queue = deque([root])​ # 先
這篇文章,會帶著大家複習以前學過的DFS框架 結合樹型DP, 並且以路徑和Path Sum的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): if base case or sto
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
今天的官方每日一題是Island Perimeter島嶼周長,很有趣的一題。 題目非常直觀好懂。也很適合拿來作為多角度複習、回顧圖論演算法的好題目。 英文的題目敘述在這裡 題目敘述 題目會給我們一個二維陣列當作地圖,格子點為1代表陸地,格子點為0代表海洋。 要求我們以四連通N4的方式拜訪
題目敘述 題目會給定一顆二元樹的根結點, 要求我們在指定的層樹d,插入新的一層,節點值為v。 原本的左、右子樹,就成為新的那一層的左子樹、右子樹。 題目的原文敘述 測試範例 Example 1: Input: root = [4,2,6,3,1,5], val = 1, depth =
這篇文章,會帶著大家複習以前學過的DFS框架, 並且以圖論的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): # 邊界條件 if base case or stop cond
這篇文章,會帶著大家複習以前學過的BFS框架, 並且以圖論的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 BFS 框架 + 演算法 虛擬碼 # Queue 通常初始化成根結點,作為起點 BFS_queue = deque([root])​ # 先
這篇文章,會帶著大家複習以前學過的DFS框架 結合樹型DP, 並且以路徑和Path Sum的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): if base case or sto
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
你可能也想看
Google News 追蹤
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
這篇文章介紹了如何判斷兩個法的關係。首先從找同分開始,若無同分則為相違關係。文章詳細說明了四種關係(三句、四句、同義、相違)的論式還原方法,並以紅色和顏色為例,展示了三句關係的辯論過程。辯論中驗證同分、周遍性和俱非三個方面,通過一系列問答來確認兩者的關係。
Thumbnail
本篇文章介紹了路徑的概念和兩種不同的路徑運用。這些知識對於網頁開發非常重要,能夠幫助網站開發者更好地管理資源文件的位置。文章通過實際例子和相對路徑的範例來解釋這些概念。希望通過這篇文章,讀者能夠清楚地瞭解路徑的概念,並在日後的開發中能夠靈活運用。
Thumbnail
上篇進一步認識基本的圖形架構與三大 Graph 算法,那首先從 shortest path 開始,我們會陸續去理解這些算法,以及可能的應用,如果還沒有看過上一篇的,可以點以下連結~那我們就開始吧! 【圖論Graph】Part1:初探圖形與圖形演算法之應用
Thumbnail
以前總會覺得有一部分的文學是藏匿的技術,比方把意義藏進意象,把思辨藏進人物和情節。現在想想自己這種說法似乎不太對勁。小說本質上應該是多線的,但因為敘事角度之故通常只會出現單線,把這句話丟進生活裡也一樣。
Thumbnail
一魚多吃 Linea + 銘文 + Particle Network,今天晚上按頭打 邀請: https://ally.build?inviteCode=AAE6J2 https://ally.build?inviteCode=QOCT15 https://ally.build?inviteC
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
這篇文章介紹了如何判斷兩個法的關係。首先從找同分開始,若無同分則為相違關係。文章詳細說明了四種關係(三句、四句、同義、相違)的論式還原方法,並以紅色和顏色為例,展示了三句關係的辯論過程。辯論中驗證同分、周遍性和俱非三個方面,通過一系列問答來確認兩者的關係。
Thumbnail
本篇文章介紹了路徑的概念和兩種不同的路徑運用。這些知識對於網頁開發非常重要,能夠幫助網站開發者更好地管理資源文件的位置。文章通過實際例子和相對路徑的範例來解釋這些概念。希望通過這篇文章,讀者能夠清楚地瞭解路徑的概念,並在日後的開發中能夠靈活運用。
Thumbnail
上篇進一步認識基本的圖形架構與三大 Graph 算法,那首先從 shortest path 開始,我們會陸續去理解這些算法,以及可能的應用,如果還沒有看過上一篇的,可以點以下連結~那我們就開始吧! 【圖論Graph】Part1:初探圖形與圖形演算法之應用
Thumbnail
以前總會覺得有一部分的文學是藏匿的技術,比方把意義藏進意象,把思辨藏進人物和情節。現在想想自己這種說法似乎不太對勁。小說本質上應該是多線的,但因為敘事角度之故通常只會出現單線,把這句話丟進生活裡也一樣。
Thumbnail
一魚多吃 Linea + 銘文 + Particle Network,今天晚上按頭打 邀請: https://ally.build?inviteCode=AAE6J2 https://ally.build?inviteCode=QOCT15 https://ally.build?inviteC