最近公共祖先 LCA 的Tarjan演算法

2022-08-09 20:39:17 字數 1509 閱讀 3846

lca(t,u,v):在有根樹t中,詢問乙個距離根最遠的結點x,使得x同時為結點u、v的祖先

lca問題可以用樸素的dfs方法解決,但是時間複雜度就很高了,這裡介紹一種高階一點的解決lca問題的tarjan演算法。

tarjan演算法是由robert tarjan在2023年發現的一種高效的離線演算法,也就是說,它要首先讀入所有的詢問(求一次lca叫做一次詢問),然後並不一定按照原來的順序處理這些詢問。

首先需要有一些預備知識:

1.基本圖論

這個就不多講了,如果有不知道的可以隨便抓一本資料結構的書惡補一下。

2.並查集

並查集其實也是很簡單的東西,實現的**都不超過10行。

這裡提一下並查集的概念,並查集是一種處理元素之間等價關係的資料結構,一開始我們假設元素都是分別屬於乙個獨立的集合裡的,主要支援兩種操作:

合併兩個不相交集合(union)

判斷兩個元素是否屬於同一集合(find)

需要知道一點,就是並查集的find操作的時間複雜度是常數級別的。

考察樹t

中所有與結點

u有關的詢問

(u, v)

對於子樹

u中的結點

v,滿足

lca(u, v) = u

對於子樹

p1而非子樹

u中的結點

v,滿足

lca(u, v) = p1

對於子樹

p2而非子樹

p1中的結點

v,滿足

lca(u, v) = p2

演算法dfs有根樹t,定義從根節點到當前正在遍歷的結點u的路徑為活躍路徑p

對於每個已經遍歷過的結點x,我們用並查集將其連線到p上距離其最近的結點find(x)

記錄與u有關的詢問集合為q(u)

對於q(u)中的任意一組詢問lca(u, v),如果v已經遍歷過,那麼答案就是find(v),所以我們只需要維護當前所有以遍歷結點的f即可。

第一次遍歷結點u時,有find(u) = u;

遍歷完子樹u後,子樹u內任意結點v均有f(v) = u;回溯回結點p1時,子樹u內任意結點v均有f(w) = p1,使用並查集完成即可。

演算法流程:

tarjan(u)

f(u)

for each (u,v) in q(u) do answer(u,v)

for each v in son(u)

a) tarjan(v);

b) f(v)

這裡的f用並查集來實現。

這樣lca的tarjan演算法就可以完整的實現了,這個演算法的時間複雜度取決於並查集find操作的時間複雜度,如果採用不相交集森林的方法來實現並查集並採用路徑壓縮來優化,這樣find操作的時間複雜度可以認為是常數級別的。

所以tarjan演算法的時間複雜度就是o(na(n) + q),a(n)在可以計算的範圍內是乙個小於4的常數,空間複雜度為o(n),其中n表示問題規模,q表示詢問次數。

最近公共祖先 LCA 最近公共祖先

直接暴力搜尋參考 普通搜尋每次查詢都需要 樸素演算法是一層一層往上找,倍增的話直接預處理出乙個 具體做法是 維護乙個 的關係來線性求出這個陣列 int anc n 31 int dep n 記錄節點深度 void dfs int u,int parent for int i 0 i g u size...

最近公共祖先 最近公共祖先(LCA)

如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。輸入格式 第一行包含三個正整數n m s,分別表示樹的結點個數 詢問的個數和樹根結點的序號。接下來n 1行每行包含兩個正整數x y,表示x結點和y結點之間有一條直接連線的邊 資料保證可以構成樹 接下來m行每行包含兩個正整數a b,表示詢問...

LCA 最近公共祖先

定義 對於有根樹t的兩個結點u v,最近公共祖先lca t,u,v 表示乙個結點x,滿足x是u v的祖先且x的深度盡可能大。另一種理解方式是把t理解為乙個無向無環圖,而lca t,u,v 即u到v的最短路上深度最小的點。現在給定乙個根為1的樹,求某兩個點的最近公共祖先。思路 預處理出每個點的深度再一...