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

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

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


英文的題目敘述在這裡


題目敘述

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

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


測試範例

Example 1:

vocus|新世代的創作平台


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:

vocus|新世代的創作平台


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
小松鼠的演算法樂園
99會員
428內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
2024/09/26
Leetcode 729. My Calendar I 給定一個行事曆的class定義和行程安排的介面interface。 請完成下列function 1.建構子MyCalendar() 初始化MyCalendar物件 2.boolean book(int start, int end) 插入新行程
Thumbnail
2024/09/26
Leetcode 729. My Calendar I 給定一個行事曆的class定義和行程安排的介面interface。 請完成下列function 1.建構子MyCalendar() 初始化MyCalendar物件 2.boolean book(int start, int end) 插入新行程
Thumbnail
2024/09/10
Insert Greatest Common Divisors in Linked List 題目給定一個鏈結串列, 請在兩兩節點之間加入一個新節點,新節點的值為兩者之間的最大公因數。 最後返回新串列的head node作為答案。
Thumbnail
2024/09/10
Insert Greatest Common Divisors in Linked List 題目給定一個鏈結串列, 請在兩兩節點之間加入一個新節點,新節點的值為兩者之間的最大公因數。 最後返回新串列的head node作為答案。
Thumbnail
2024/09/09
2326. Spiral Matrix IV 題目給定一個Linked list和對應的矩陣高度m、寬度n。 請依照順時針的拜訪順序, 從左上角出發,依照次序把Linked List的內容填到矩陣裡。 如果有剩餘不足的空位,就填補-1。 最後將填補好的矩陣返回作為答案。
Thumbnail
2024/09/09
2326. Spiral Matrix IV 題目給定一個Linked list和對應的矩陣高度m、寬度n。 請依照順時針的拜訪順序, 從左上角出發,依照次序把Linked List的內容填到矩陣裡。 如果有剩餘不足的空位,就填補-1。 最後將填補好的矩陣返回作為答案。
Thumbnail
看更多
你可能也想看
Thumbnail
這篇文章,會帶著大家複習以前學過的格子點DP框架, 並且以移動路徑Unique Path的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 格子點DP框架 依循題目的定義和規則,找出格子點移動的共同模式。 以本篇文章的例題為例,每一步可以選擇往右走一個
Thumbnail
這篇文章,會帶著大家複習以前學過的格子點DP框架, 並且以移動路徑Unique Path的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 格子點DP框架 依循題目的定義和規則,找出格子點移動的共同模式。 以本篇文章的例題為例,每一步可以選擇往右走一個
Thumbnail
子集合生成是一道經典的組合類上機考和面試題目。本篇文章介紹多個不同的解決方案,以及相關演算法框架。主要目標是給定n個相異的元素,產生所有的子集合。
Thumbnail
子集合生成是一道經典的組合類上機考和面試題目。本篇文章介紹多個不同的解決方案,以及相關演算法框架。主要目標是給定n個相異的元素,產生所有的子集合。
Thumbnail
題目敘述 給定兩個字串word1和word2,每次操作時,可以有三個選項 插入一個字元 刪除一個字元 替換一個字元 請問把word1轉換成word2的最小操作次數是多少? 題目的原文敘述 約束條件 Constraints: 0 <= word1.length, word2.le
Thumbnail
題目敘述 給定兩個字串word1和word2,每次操作時,可以有三個選項 插入一個字元 刪除一個字元 替換一個字元 請問把word1轉換成word2的最小操作次數是多少? 題目的原文敘述 約束條件 Constraints: 0 <= word1.length, word2.le
Thumbnail
今天的官方每日一題是Island Perimeter島嶼周長,很有趣的一題。 題目非常直觀好懂。也很適合拿來作為多角度複習、回顧圖論演算法的好題目。 英文的題目敘述在這裡 題目敘述 題目會給我們一個二維陣列當作地圖,格子點為1代表陸地,格子點為0代表海洋。 要求我們以四連通N4的方式拜訪
Thumbnail
今天的官方每日一題是Island Perimeter島嶼周長,很有趣的一題。 題目非常直觀好懂。也很適合拿來作為多角度複習、回顧圖論演算法的好題目。 英文的題目敘述在這裡 題目敘述 題目會給我們一個二維陣列當作地圖,格子點為1代表陸地,格子點為0代表海洋。 要求我們以四連通N4的方式拜訪
Thumbnail
今天的一魚三吃系列是透過 兩點之間是否存在一條路徑的題目,來回顧以前學過的DFS、BFS和Disjoint Set,鞏固圖論演算法的知識點。 英文的題目敘述在這裡 題目敘述 給定我們已知n個節點的圖,和圖上的每一條無向邊edges。 請問給定的起點start和終點end是否存在一條路徑可
Thumbnail
今天的一魚三吃系列是透過 兩點之間是否存在一條路徑的題目,來回顧以前學過的DFS、BFS和Disjoint Set,鞏固圖論演算法的知識點。 英文的題目敘述在這裡 題目敘述 給定我們已知n個節點的圖,和圖上的每一條無向邊edges。 請問給定的起點start和終點end是否存在一條路徑可
Thumbnail
這篇文章,會帶大家快速回顧DFS+回溯法框架(還沒看過或想複習的可以點連結進去)。 用DFS+回溯法框架,解開 直線排列Permutations 的全系列題目。 幫助讀者鞏固DFS+回溯法框架這個重要的知識點。 回顧 DFS+回溯法框架 白話的意思 # 列舉所有可能的情況,遞迴展開所有分
Thumbnail
這篇文章,會帶大家快速回顧DFS+回溯法框架(還沒看過或想複習的可以點連結進去)。 用DFS+回溯法框架,解開 直線排列Permutations 的全系列題目。 幫助讀者鞏固DFS+回溯法框架這個重要的知識點。 回顧 DFS+回溯法框架 白話的意思 # 列舉所有可能的情況,遞迴展開所有分
Thumbnail
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
5 月將於臺北表演藝術中心映演的「2026 北藝嚴選」《海妲・蓋柏樂》,由臺灣劇團「晃晃跨幅町」製作,本文將以從舞台符號、聲音與表演調度切入,討論海妲・蓋柏樂在父權社會結構下的困境,並結合榮格心理學與馮.法蘭茲對「阿尼姆斯」與「永恆少年」原型的分析,理解女人何以走向精神性的操控、毀滅與死亡。
Thumbnail
5 月將於臺北表演藝術中心映演的「2026 北藝嚴選」《海妲・蓋柏樂》,由臺灣劇團「晃晃跨幅町」製作,本文將以從舞台符號、聲音與表演調度切入,討論海妲・蓋柏樂在父權社會結構下的困境,並結合榮格心理學與馮.法蘭茲對「阿尼姆斯」與「永恆少年」原型的分析,理解女人何以走向精神性的操控、毀滅與死亡。
Thumbnail
這篇文章,會帶著大家複習以前學過的DFS框架 結合樹型DP, 並且以路徑和Path Sum的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): if base case or sto
Thumbnail
這篇文章,會帶著大家複習以前學過的DFS框架 結合樹型DP, 並且以路徑和Path Sum的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): if base case or sto
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
Thumbnail
這篇文章,會帶著大家複習以前學過的DFS框架, 並且以圖論的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): # 邊界條件 if base case or stop cond
Thumbnail
這篇文章,會帶著大家複習以前學過的DFS框架, 並且以圖論的應用題與概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 DFS 深度優先搜索框架 def dfs( parameter ): # 邊界條件 if base case or stop cond
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News