假設你要在幾個村莊之間鋪水管,想用最少的管線總長把大家全接通、一寸都不浪費——這就是最小生成樹要解的事;Prim 的做法很直覺:從一個村莊出發,每次都挑「接上去最便宜的那一段」,慢慢把整張網路長大。另一種問題不太一樣:在一張「誰指向誰」的有向關係圖裡(像網頁互連、誰追蹤誰),哪些點能彼此繞得回對方、圈成一個小團體?Tarjan 強連通分量只走一趟就把這些圈子一次框出來。下面兩區可以各自逐步播放,看它們怎麼一步步長樹、圈組。
兩個經典但方向不同的圖論演算法:Prim 最小生成樹作用於無向帶權圖,以優先佇列貪心每輪擴充一個「離目前生成樹最近」的節點, 逐步長出一棵涵蓋所有節點、總權重最小的樹;Tarjan 強連通分量(SCC)作用於有向圖, 單次 DFS 搭配 dfn(發現序)/low(可回溯到的最早祖先)與一個顯式棧,在回溯時一次性彈出整組互相可達的節點。 一個是「貪心累加」,一個是「單趟 DFS 分組」。
以節點 0 為起點,key[0]=0 並將 (0,0) 放入優先佇列;反覆取出佇列中 (w,node) 字典序最小者,若該節點已在 MST 中則跳過(lazy deletion,不主動從佇列刪除舊項); 否則將其併入 MST、累加權重,並檢查其每個不在 MST 中的鄰居,只要邊權小於該鄰居目前的 key 值就更新 key、記錄父節點並重新入佇列。 直到佇列清空,累積的父邊即為最小生成樹。
單次 DFS:進入節點 u 時設 dfn[u]=low[u]=time++、將 u 入棧;依 id 升序檢查每個鄰居 v——若 v 未訪問則遞迴後用 low[v] 更新 low[u], 若 v 已訪問且仍在棧上則用 dfn[v] 更新 low[u](代表 v 是還沒結束的祖先,形成環)。 回溯到 u 時若 low[u]==dfn[u],代表 u 是目前這組強連通分量的「根」,將棧一路彈出至 u,彈出的節點即組成一組 SCC。