[LCA] tarjan演算法 模版
阿新 • • 發佈:2019-01-26
LCA演算法:
LCA(Least Common Ancestor),顧名思義,是指在一棵樹中,距離兩個點最近的兩者的公共節點。也就是說,在兩個點通往根的道路上,肯定會有公共的節點,我們就是要求找到公共的節點中,深度儘量深的點。還可以表示成另一種說法,就是如果把樹看成是一個圖,這找到這兩個點中的最短距離。
tarjan演算法是離線演算法,複雜度為O(n+Q),使用了並查集+dfs的操作。中間的那個並查集操作的作用,只是將已經查詢過的節點捆成一個集合然後再指向一個公共的祖先。另外,如果要查詢LCA(a,b),必須把(a,b)和(b,a)都加入鄰接表。
如poj1330為例
#include <iostream> #include <cstdio> #include <cstring> #include <vector> using namespace std; #define MAXN 10001 int n,fa[MAXN]; int rank[MAXN]; int indegree[MAXN]; int vis[MAXN]; vector<int> hash[MAXN],Qes[MAXN]; int ances[MAXN];//祖先 void init(int n) { for(int i=0;i<=n;i++) { fa[i]=i; rank[i]=0; indegree[i]=0; vis[i]=0; ances[i]=0; hash[i].clear(); Qes[i].clear(); } } int find(int x) { if(x != fa[x]) fa[x]=find(fa[x]); return fa[x]; } void unio(int x,int y) { int fx=find(x),fy=find(y); if(fx==fy) return ; if(rank[fy]<rank[fx]) fa[fy]=fx; else { fa[fx]=fy; if(rank[fx]==rank[fy]) rank[fy]++; } } void Tarjan(int u) { ances[u]=u; int i,size = hash[u].size(); for(i=0;i<size;i++) { Tarjan(hash[u][i]);//遞迴處理兒子 unio(u,hash[u][i]);//將兒子父親合併,合併時會將兒子的父親改為u ances[find(u)]=u;//此時find(u)仍為u,即 } vis[u]=1; //查詢 size = Qes[u].size(); for(i=0;i<size;i++) { if(vis[Qes[u][i]]==1)//即查詢的另一個結點開始已經訪問過,當前的u在此回合訪問。 { printf("%d\n",ances[find(Qes[u][i])]);//由於遞迴,此時還是在u return; } } } int main() { int t; int i,j; scanf("%d",&t); while(t--) { scanf("%d",&n); init(n); int s,d; for(i=1;i<=n-1;i++) { scanf("%d%d",&s,&d); hash[s].push_back(d); indegree[d]++; } scanf("%d%d",&s,&d); // if(s==d)//如果需要計數的時候注意 // ans[d]++; // else // { Qes[s].push_back(d); Qes[d].push_back(s); // } for(j=1;j<=n;j++) { if(indegree[j]==0) { Tarjan(j); break; } } } return 0; }