Tarjan離線演算法求最近公共祖先(LCA)

2021-08-28 16:11:49 字數 2804 閱讀 4821

**:

arjan離線演算法求lca介紹

前言:首先,本人搞懂tarjan求最近公共祖先(lca),也是瀏覽了大量其他網友大牛的文章,若是看了本文仍未弄懂的,可以嘗試自己做一下模板題(裸題)hdu2586,自己用資料去感受一下,或者可以換篇文章再看,或許他的文章更對你的「胃口」。

一:概念介紹

1:最近公共祖先

對於有根樹tree的兩個結點u、v,其最近公共祖先lca表示乙個結點x,滿足x是u、v的祖先且x的深度盡可能大。另一種理 解方式是把tree理解為乙個無向無環圖,而其最近公共祖先lca即u到v的最短路上深度最小的點。

2:並查集:詳見

3:離線演算法:

演算法設計策略都是基於在執行演算法前輸入資料已知的基本假設,也就是說,對於乙個離線演算法,在開始時就需要知道問題的所有輸入資料,而且在解決乙個問題後就要立即輸出結果,通常將這類具有問題完全資訊前提下設計出的演算法成為離線演算法( off line algorithms)

二:tarjan離線演算法求lca

首先根據lca的定義,我們可以想到一種最簡單的方法來求u,v的lca:分別從u,v開始向根節點走,當這兩個點走到第乙個相同的節點時,這個節點就是lca(u,v)

但是這樣求效率不夠,求一次的最壞的時間複雜度就是o(n),若是再多來幾個詢問就會超時

現在介紹一種當詢問次數為q,節點數為n時時間複雜度為o(n+q)的離線求lca的演算法:tarjan演算法

這種演算法是基於dfs和並查集來實現的。設fa[x]為x的父親,dist[x]為x節點到根節點的距離。首先從一號根節點(記為u)開始訪問他的每乙個子節點(記為v),並用根節點與當前訪問的子節點的距離更新dist值,即dist[v]=dist[u]+map[v][u],其中map[v][u]表示v到u的距離,然後將當前子節點當做根點用上述同樣步驟遞迴下去,並在遞迴回溯後將其fa[v]值更新,這樣的目的是保證子節點v的所有子樹全部被訪問過。

現在我們需知道第k個詢問的lca是什麼,那麼這個操作應在詢問中兩個節點的子樹全部訪問完的基礎上再進行。對於現在狀態的根點u,訪問它的子節點v,若v點「作過」根點,即被遞迴過,才能保證v的所有子樹被全部訪問完,這時才能將與之有關的詢問《即詢問中包含v點》更新其lca=get(v),get為並查集,即找到v所在集合的起點,因為並查集在這裡的作用就是將同一子樹中的子節點的父親指向該子樹的根節點,相當於歸為了乙個集合,這個集合的起點就是當前子樹的根節點。

這樣,從1號根節點出發,向下遞迴直到到達葉子節點位置,樹中每個節點都被訪問過了一次,在回溯後,為了對每個詢問(記總詢問次數為q)更新lca值訪問了相關點,則時間複雜度為o(n)+o(q),因此這是個o(n+q)的演算法!

實現**:

這裡實現是用臨接鍊錶存的根與子節點的關係,b陣列中存的是詢問資訊,a陣列存的是邊的資訊,下面的**為tarjan的主函式,若沒看懂陣列的含義,請接著看下面例題hdu2586

void tarjan(int x)

for(int i=edge[x];i;i=a[i].next)} }

:例題分析:hdu 2586 《how far away ?》題目大意】:

乙個村子裡有n個房子,這n個房子用n-1條路連線起來,接下來有m次詢問,每次詢問兩個房子a,b之間的距離是多少。

【分析】:

這是個求最近公共祖先的問題,用臨接煉表存下每條邊和詢問的資訊,然後跑一遍tarjan,最後對於詢問i,j的lca,答案為dist[i]+dist[j]-2*dist[lca(i,j)],這裡dist[i]為i點到1號根節點的距離

【**】:用時31ms,好像將更新lca操作整體放後面也行

#include#include#include#include#include#include#include#includeusing namespace std;

#define maxedge 100001

#define maxn 40001

#define maxm 410

struct edge;

struct question;

edge a[maxedge];

question b[maxm];

int data,n,m,edge[maxn],question[maxn],tot1=0,tot2=0,ansf[maxm],anst[maxm];

int fa[maxn],lca[maxm],dist[maxn];//dist[i]記錄了i點到根節點的距離

bool vis[maxn];

void add_edge(int x,int y,int value)

void add_question(int x,int y,int number)

int get(int x)

void tarjan(int x)

for(int i=edge[x];i;i=a[i].next)

} }int main()

for(int i=1;i<=m;i++)

tarjan(1);

for(int i=1;i<=m;i++)

printf("%d\n",dist[ansf[i]]+dist[anst[i]]-2*dist[lca[i]]);

}//system("pause");

return 0;

}

Tarjan離線演算法 (LCA最近公共祖先)

tarjan離線演算法是利用並查集和dfs來達到離線處理的目的 我們都知道,對於一棵樹,後序遍歷一遍,訪問它的根的時機一定是後與它的孩子的。換一句話,當開始遍歷它的根節點的時候,它遍歷過的孩子的公共祖先一定是這個根而這也就成為了我們解題的思想。由於是需要對整樹進行dfs,所以tarjan需要在所有資...

LCA 最近公共祖先 離線演算法之tarjan

初步學習了一下用tarjan演算法求最近公共祖先 lca 下面是敝人的拙見 tarjan是乙個離線演算法,所謂離線演算法就是在所有詢問均儲存完之後再做操作。而tarjan演算法對這些詢問並不一定按儲存時的順序去操作,這就是tarjan演算法的時間複雜度能達到o n q 的關鍵所在 q為詢問的數量 至...

Tarjan演算法求LCA(最近公共祖先)

lca的離線演算法。複雜度為o n q 這個演算法充分利用了dfs樹的結構。對於每個節點u,關於它的詢問 u,v 只有兩種。假設先dfs u 後dfs v 1 v在u的子樹內。此時lca u,v u.2 v不在u的子樹內。假設v在u的父親的另一棵子樹內。此時lca u,v father u 如果不滿...