1. 程式人生 > >tarjan算法求最近公共祖先

tarjan算法求最近公共祖先

技巧 splay 路徑壓縮 tor 沒有 blog 分析 father mar

tarjian算法

LCA: LCA(Least Common Ancestor),顧名思義,是指在一棵樹中,距離兩個點最近的兩者的公共節點。也就是說,在兩個點通往根的道路上,肯定會有公共的節點,我們就是要求找到公共的節點中,深度盡量深的點。還可以表示成另一種說法,就是如果把樹看成是一個圖,這找到這兩個點中的最短距離。

LCA算法有在線算法也有離線算法,所謂的在線算法就是實時性的,而離線算法則是要求一次性讀入所有的請求,然後在統一得處理。而在處理的過程中不一定是按照請求的輸入順序來處理的。說不定後輸入的請求在算法的執行過程中是被先處理的。

tarjan算法。這個算法是基於並查集和DFS的。離線算法。

現在我們來觀察正在處理與x結點關聯的詢問時並查集的情況。由於一個結點處理完畢後,它就被歸到其父結點所在的集合,所以在已經處理過的結點中(包括 x本身),x結點本身構成了與x的LCA是x的集合,x結點的父結點及以x的所有已處理的兄弟結點為根的子樹構成了與x的LCA是father[x]的集合,x結點的父結點的父結點及以x的父結點的所有已處理的兄弟結點為根的子樹構成了與x的LCA是father[father[x]]的集合……(上面這幾句話如果看著別扭,就分析一下句子成分,也可參照右面的圖)假設有一個詢問(x,y)(y是已處理的結點),在並查集中查到y所屬集合的根是z,那麽z 就是x和y的LCA,x到y的路徑長度就是lv[x]+lv[y]-lv[z]*2。累加所有經過的路徑長度就得到答案。  現在還有一個問題:上面提到的詢問(x,y)中,y是已處理過的結點。那麽,如果y尚未處理怎麽辦?其實很簡單,只要在詢問列表中加入兩個詢問(x, y)、(y,x),那麽就可以保證這兩個詢問有且僅有一個被處理了(暫時無法處理的那個就pass掉)。而形如(x,x)的詢問則根本不必存儲。  如果在並查集的實現中使用路徑壓縮等優化措施,一次查詢的復雜度將可以認為是常數級的,整個算法也就是線性的了。

Tarjan作為離線off-line算法,在程序開始前,需要將所有等待詢問的節點對提前存儲,然後程序從樹根開始執行TarjanLCA()。假如有如下一棵多叉樹

技術分享

根據TarjanLCA的實現算法可以看出,只有當某一棵子樹全部遍歷處理完成後,才將該子樹的根節點標記為黑色(初始化是白色),假設程序按上面的樹形結構進行遍歷,首先從節點1開始,然後遞歸處理根為2的子樹,當子樹2處理完畢後,節點2, 5, 6均為黑色;接著要回溯處理3子樹,首先被染黑的是節點7(因為節點7作為葉子不用深搜,直接處理),接著節點7就會查看所有詢問(7, x)的節點對,假如存在(7, 5),因為節點5已經被染黑,所以就可以斷定(7, 5)的最近公共祖先就是find(5).ancestor,即節點1(因為2子樹處理完畢後,子樹2和節點1進行了union,find(5)返回了合並後的樹的根1,此時樹根的ancestor的值就是1)。 有人會問如果沒有(7, 5),而是有(5, 7)詢問對怎麽處理呢?我們可以在程序初始化的時候做個技巧,將詢問對(a, b)和(b, a)全部存儲,這樣就能保證完整性。

技術分享

tarjan算法求最近公共祖先