最近公共祖先 LCA
Tarjan演算法
前向星型
#include<stdio.h>
#define N 500000
struct Edge{
int to,next,w;
}edge[N],q[N]; //q陣列儲存查詢的兩點
int num_edge,num_q,head[N],qhead[N];
int pre[N],vis[N];//pre[i]儲存i的父節點
//建立連結串列 新增邊
void add_edge(int from,int to)
{
edge[++num_edge].next=head[from];//以from結點出發的上一條邊在陣列中的編號
edge[num_edge].to=to;
head[from]=num_edge;
}
void add_q(int from,int to)
{
q[++num_q].next=qhead[from];
q[num_q].to=to;
qhead[from]=num_q;
}
int find(int x)//尋找x的父親結點
{
while(pre[x]!=x)
x=pre[x];
return x;
}
int dfs(int x)
{
pre[x]=x;
vis[x]=1;
//遍歷與x相連的結點
for(int k=head[x];k;k=edge[k].next){
int v=edge[k].to;
if(!vis[v]){//如果未被搜尋
dfs(v);//以該結點為根節點,繼續搜尋
pre[v]=x;//把v的父親結點設為x
}
}
//搜尋包含結點x的所有查詢
for(int k=qhead[x];k;k=q[k].next){
int v=q[k].to;
if(vis[v]){//如果另一結點已被搜尋過
q[k].w=find(v);//把另一節點的祖先設為這兩個節點的最近公共祖先
if(k%2)//由於將每一組查詢變為兩組,所以2n-1和2n的結果是一樣的
q[k+1].w=q[k].w;
else
q[k-1].w=q[k].w;
}
}
}
int main()
{
int n,m,p,x,y,i;
scanf("%d%d%d",&n,&m,&p);//輸入結點數,查詢數和根結點
for(i=1;i<n;i++){
scanf("%d%d",&x,&y);
add_edge(x,y);
add_edge(y,x);
}
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);//輸入每次查詢
add_q(x,y);
add_q(y,x);
}
dfs(p);//進入以p為根節點的樹的深搜
for(i=1;i<=m;i++)
printf("%d ",q[i*2].w);//兩組結果一樣,輸出一組即可
return 0;
}
暴力求LCA
//暴力求LCA
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=10010;
vector<int>g[N];//鄰接表
int depth[N],f[N],in[N];//depth儲存各結點的深度,f儲存各結點的父節點
void dfs(int rt,int fa)//分別表示本結點和本結點的父親結點
{
depth[rt]=depth[fa]+1;
f[rt]=fa;
for(int i=0;i<g[rt].size();i++)
dfs(g[rt][i],rt);
}
int LCA(int a,int b)
{
if(depth[a]>depth[b])
swap(a,b);
while(depth[b]>depth[a])
b=f[b];
while(a!=b)
a=f[a],b=f[b];
return a;
}
int main()
{
int t,n,a,b;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=0;i<n;i++)
g[i].clear();
memset(in,0,sizeof(in));
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
g[a].push_back(b);
in[b]++;//b的入度加一
}
depth[0]=-1;
int rt=0;
for(int i=1;i<=n&&rt==0;i++)//尋找樹的根結點
if(in[i]==0)
rt=i;
dfs(rt,0);
scanf("%d%d",&a,&b);
printf("%d\n",LCA(a,b));
}
return 0;
}
倍增演算法求LCA
//倍增演算法求LCA
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=10010;
vector<int>g[N];//鄰接表
int depth[N],f[N][20],in[N];//depth儲存各結點的深度,f儲存各結點的父節點
void dfs(int rt,int fa)//分別表示本結點和本結點的父親結點
{
depth[rt]=depth[fa]+1;
f[rt][0]=fa;
for(int i=1;i<20;i++)
f[rt][i]=f[f[rt][i-1]][i-1];
for(int i=0;i<g[rt].size();i++)
dfs(g[rt][i],rt);
}
int LCA(int a,int b)
{
if(depth[a]>depth[b])
swap(a,b);
for(int i=19;i>=0;i--)
if(depth[b]-(1<<i)>=depth[a])
b=f[b][i];
if(a==b)
return a;
for(int i=19;i>=0;i--)
if(f[b][i]!=f[a][i])
a=f[a][i],b=f[b][i];
return f[b][0];
}
int main()
{
int t,n,a,b;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=0;i<n;i++)
g[i].clear();
memset(in,0,sizeof(in));
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
g[a].push_back(b);
in[b]++;//b的入度加一
}
depth[0]=-1;
int rt=0;
for(int i=1;i<=n&&rt==0;i++)//尋找樹的根結點
if(in[i]==0)
rt=i;
dfs(rt,0);
scanf("%d%d",&a,&b);
printf("%d\n",LCA(a,b));
}
return 0;
}
題目集錦
相關文章
- LCA最近公共祖先
- Day 5 LCA 最近公共祖先
- 演算法學習筆記(5): 最近公共祖先(LCA)演算法筆記
- POJ 1330 LCA最近公共祖先 離線tarjan演算法演算法
- 最近公共祖先
- 樹上公共祖先(LCA)
- 樹上問題/簡單演算法 LCA【最近公共祖先】演算法
- LeetCode——最近公共祖先LeetCode
- Google S2 中的四叉樹求 LCA 最近公共祖先Go
- 樹的最近公共祖先問題
- 二叉樹的最近公共祖先二叉樹
- 「學習筆記」tarjan 求最近公共祖先筆記
- Git 中的演算法-最近公共祖先Git演算法
- 0235-二叉搜尋樹的最近公共祖先
- leetcode 235. 二叉搜尋樹的最近公共祖先LeetCode
- (117)235. 二叉搜尋樹的最近公共祖先(leetcode)LeetCode
- # 劍指 Offer 68 - II. 二叉樹的最近公共祖先二叉樹
- 二叉搜尋樹和二叉樹的最近公共祖先二叉樹
- 【leetcode 簡單】 第六十八題 二叉搜尋樹的最近公共祖先LeetCode
- lc235.二叉搜尋樹的最近公共祖先【①分別得到祖先序列,然後比較;②***同時查詢,找出分岔結點】
- 236、二叉樹的最近公共祖先 | 演算法(leetcode,附思維導圖 + 全部解法)300題二叉樹演算法LeetCode
- 二叉樹:距離最近的共同祖先二叉樹
- 8.3考試總結(NOIP模擬19)[最長不下降子序列·完全揹包問題·最近公共祖先]
- LeetCode 236. 二叉樹的最近公共祖先 極限效能演算法 比LeetCode 99%還快50倍LeetCode二叉樹演算法
- JZ-076-樹中兩個節點的最低公共祖先
- lca
- 求 LCA
- 程式碼隨想錄day18 || 530 二叉搜尋樹最小差,501 二叉搜尋樹眾數,236 二叉搜尋樹最近公共祖先
- RMQ求lcaMQ
- Day21 | 530.二叉搜尋樹的最小絕對差、501.二叉搜尋樹中的眾數 、236. 二叉樹的最近公共祖先二叉樹
- DFN 序求 LCA
- 求解 LCA の方法
- LCA(倍增與Tarjan)
- LCA + 樹上倍增
- 祖先樹統計
- jQuery 遍歷 – 祖先jQuery
- LCA學習筆記筆記
- HDU 2586 倍增 / Tarjan LCA