在求解最近公共祖先為問題上,用到的是tarjan的思想,從根結點開始形成一棵深搜樹,非常好的處理技巧就是在回溯到結點u的時候,u的子樹已經遍歷,這時候才把u結點放入合併集合中,這樣u結點和所有u的子樹中的結點的最近公共祖先就是u了,
u和還未遍歷的所有u的兄弟結點及子樹中的最近公共祖先就是u的父親結點。以此類推。。這樣我們在對樹深度遍歷的時候就很自然的將樹中的結點分成若干的集合,兩個集合中的所屬不同集合的任意一對頂點的公共祖先都是相同的,也就是說這兩個集合的最近公共最先只有乙個。對於每個集合而言可以用並查集來優化,時間複雜度就大大降低了,為o(n + q),n為總結點數,q為詢問結點對數。
從根開始深搜遍歷樹,每當回溯到乙個節點時,那就意味著我們已經完成了該節點子樹的遍歷,顯然這個節點就是子樹中點以及其本身的最近公共祖先,以此類推到整個樹。對於乙個點,只有完成了其子樹的遍歷,我們才改變其 父節點 的值(賦初值為father[ i ] = i),這樣,對於每次詢問(就是給出兩點標號,要求求出兩點間最短距離,算一次詢問。假設為 a 和 b),我們搜到 a (或b)時,如果 b (或a)已經被訪問過,那麼向上回溯,直到第乙個 父節點 已經改變的點,就是包括點 a 和點 b 在內子樹的根,這就是我們要找的 a 和 b 的最近公共祖先。
//parent為並查集,find為並查集的查詢操作
//query為詢問結點對集合
//tree為基圖有根樹
tarjan(u)
visit[u] = true
for each (u, v) in query
if visit[v]
ans(u, v) = find(v)
for each (u, v) in tree
if !visit[v]
tarjan(v)
parent[v] = u
/*
hdu 2586 最近公共祖先lca
不懂。。。
深搜遍歷樹,每當回溯到乙個節點時,那就已經完成了該節點子樹的遍歷,
顯然這個節點就是子樹中點以及其本身的最近公共祖先,以此類推到整個樹。
對於乙個點,只有完成了其子樹的遍歷,我們才改變其父節點的值..
設為 a 和 b,我們搜到a或b時,如果b或a已經被訪問過,那麼向上回溯,直到第乙個父節點已經改變的點,
就是包括點a和點b在內子樹的根,這就是我們要找的a和b的最近公共祖先。
*/#include#includeusing namespace std;
#define n 40001
struct edgeedge[2*n];
int n,m,e_num,head[n];
int x[n],y[n],z[n],pre[n],dist[n],vis[n];
void addedge(int a,int b,int c)//b->a a->b
int find(int x)
void tarjan(int k)//從1開始
for(i=head[k];i!=-1;i=edge[i].next)//查詢下乙個
} }int main()
for(i=1;i<=n;i++)
x[i]=y[i]=z[i]=0;
for(i=1;i<=m;i++)
memset(vis,0,sizeof(vis));
dist[1]=0;
tarjan(1);
for(i=1;i<=m;i++)
printf("%d\n",dist[x[i]]+dist[y[i]]-2*dist[z[i]]);
} return 0;
}
最近公共祖先 python 最近公共祖先
lca演算法樸素演算法 也就是我們所說的暴力演算法,大致的思路是從樹根開始,往下迭代,如果當前結點比兩個結點都小,那麼說明要從樹的右子樹中找 相反則從左子樹中查詢 直到找到乙個結點在當前結點的左邊,乙個在右邊,說明當前結點為最近公共祖先,如果乙個結點是另外乙個結點的祖先,那麼返回前面結點的父親結點即...
最近公共祖先 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,表示詢問...