初步學習了一下用tarjan演算法求最近公共祖先(lca),下面是敝人的拙見:
tarjan是乙個離線演算法,所謂離線演算法就是在所有詢問均儲存完之後再做操作。而tarjan演算法對這些詢問並不一定按儲存時的順序去操作,這就是tarjan演算法的時間複雜度能達到o(n+q)的關鍵所在(q為詢問的數量),至於是按什麼順序,待讀者看完文章自然會明白。
首先我們來看到tarjan演算法的原理:
既然要求lca,自然是樹形結構。首先講一下,tarjan演算法是基於dfs和並查集的,dfs用於查詢待求節點的lca,並查集用於儲存結點間的父子關係。現在我們來看一棵樹:
對於這顆樹,假設現在我們要求結點 e 和結點 f 的lca,顯然是 d 。那麼為什麼 d 會是 e 和 f 的lca呢?我們來思考一下,把 d 看作根節點作為一顆單獨的樹,e和f都是d結點的後代,所以d自然史e、f的最近公共祖先。可為什麼不是b呢?e、f也是b的後代啊,當然是因為d最近啦。所以現在我們的任務就是找到一顆樹(大樹小樹不管什麼樹都行),事得待求的兩個節點同時存在於該樹下。至於如何找,前面講到tarjan演算法是基於dfs的。我們現在來模擬一下dfs找lca的過程,首先從根節點a開始,向下深搜;搜到b,則此時可把b當作一棵樹(本來也是一棵樹)向下深搜,這裡需要記錄的是我們當前是在b這顆樹下搜尋。知道搜尋到d的時候,在以d為基礎向下搜尋的時候我們同時搜尋到了待求點e、f,那麼所求的lca就是d了。dfs部分就到這了,下面是並查集部分。我們每搜到一棵樹,都將其的祖先修改為本身,也就是自成一派。而在將這棵樹的所有子樹全都搜尋完之後,如果找到了待求點,則此時記錄的最近祖先就是該子樹樹根。隨後再將其祖先修改為原本邏輯上的父親,以此遞迴搜尋。如果赤裸裸的文字難以理解的話,也可能是我描述不清,下面上赤裸裸的**,跟著**的思路自己寫一遍的話很快就能學會了。
void tarjan(int x)
}int
size=son[x].size();
//son也是乙個vector陣列,儲存x邏輯上的後代,即儲存樹形結構
for(int i=0; i
}
到此,基本的tarjan 思路就已經理清,也可能博主語言表達能力有限並沒有闡述清楚,還望批評指正。在學習演算法的過程中,如果單純的看文字描述無法理解的話,其實可以先找一道裸題自己敲一敲,即使是對著題解或者模板敲也是有用的。這裡提供一道裸題:codevs10 最近公共祖先lca離線
利用dfs和並查集實現點對的最近公共祖先,首先遞迴到葉子節點,然後在返回時才確定父子關係,按照這樣的順序確定父子關係之後,最早出現的公共父節點便是最近公共祖先。include using namespace std const int maxn 1000 const int maxm 1000 in...
Tarjan離線演算法 (LCA最近公共祖先)
tarjan離線演算法是利用並查集和dfs來達到離線處理的目的 我們都知道,對於一棵樹,後序遍歷一遍,訪問它的根的時機一定是後與它的孩子的。換一句話,當開始遍歷它的根節點的時候,它遍歷過的孩子的公共祖先一定是這個根而這也就成為了我們解題的思想。由於是需要對整樹進行dfs,所以tarjan需要在所有資...
Tarjan離線演算法求最近公共祖先(LCA)
arjan離線演算法求lca介紹 前言 首先,本人搞懂tarjan求最近公共祖先 lca 也是瀏覽了大量其他大牛的文章,若是看了本文仍未弄懂的,可以嘗試自己做一下模板題 裸題 hdu2586,自己用資料去感受一下,或者可以換篇文章再看,或許他的文章更對你的 胃口 一 概念介紹 1 最近公共祖先 對於...