转载自:https://blog.csdn.net/ZCY19990813/article/details/81357251

LCA 最近公共祖先  此博客主要介绍 Tajan(现在只了解这个算法)

需要用到很多铺垫  比如前向星了  链式前向星了  并查集了   下面我们一一来介绍一下

首先看一下前向星 

前向星是一种数据结构,以储存边的方式来存储图  用到两个数组 len[i]是来表示以i为起点边的个数  head[i]是表示i点第一条边的下标

用前向星时要先按照起点大小排序  会增加很多复杂度  所以引申了链式前向星

链式前向星

首先需要定义一个结构体

struct EDGE{int next;   //下一条边的存储下标(初值为-1) int to;     //这条边的终点 int w;      //权值
}edge[500010];

1 结构体数组edge存边,edge[i]表示第i条边,

2 head[i]存以i为起点的第一条边(在edge中的下标)

主要代码

typedef long long ll;
void add(ll u, ll v, ll w) {  //起点u, 终点v, 权值w //cnt为边的计数,从1开始计 edge[cnt].to = v;edge[cnt].next = head[u];edge[cnt].w = w; head[u] = cnt++;    //第一条边为当前边
}

举个例子 输入起点  终点(先不考虑权值)

1 2
2 3
3 4
1 3
4 1
1 5
4 5

按照代码走一遍  会出现下表

下标 to next head
1 2 -1 head[1]=1
2 3 -1 head[2]=2
3 4 -1

head[3]=3;

4 3 1 head[1]=4
5 1 -1 head[4]=5
6 5 4 head[1]=6
7 5 5 head[4]=7

遍历以u为起点的边

for(int i=head[u];i!=-1;i=edge[i].next)

下面一个简单输出的代码  如有问题  欢迎私信

输入

4 5
1 2 1
2 3 3
2 4 2
4 3 2
1 4 22

输出

Start: 2
End: 4
W: 2Start: 2
End: 3
W: 3

代码:

#include <iostream>
using namespace std;#define MAXM 500010
#define MAXN 10010struct EDGE
{int next;   //下一条边的存储下标int to;     //这条边的终点int w;      //权值
}edge[MAXM];int n, m, cnt;
int head[MAXN];  //head[i]表示以i为起点的第一条边void Add(int u, int v, int w)    //起点u, 终点v, 权值w
{edge[cnt].to = v;edge[cnt].next = head[u];edge[cnt].w = w;head[u] = cnt++;    //第一条边为当前边
}
void Print()
{int st;cout << "Begin with[Please Input]: \n";cin >> u;for(int i=head[u]; i!=0; i=edge[i].next)  //i开始为第一条边,每次指向下一条(以-1为结束标志){cout << "Start: " << u << endl;cout << "End: " << edge[i].to << endl;cout << "W: " << edge[i].w << endl << endl;}
}int main()
{int s, t, w;cin  >>n >> m;//n为几个点 m为几条边for(int i=1; i<=m; i++){cin >> s >> t >> w;Add(s, t, w);}Print();return 0;
}

链式前向星介绍完啦  要想用lca求最近公共祖先还差一个并查集

并查集  是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题

并查集的基本操作有两个:

  merge(x, y):把元素x 和元素y 所在的集合合并,要求x 和y 所在的集合不相交,如果相交则不合并。

  find(x):找到元素x 所在的集合的代表,该操作也可以用于判断两个元素是否位于同一个集合,只要将它们各自的代表比较一下就可以了。

此代码摘抄自师哥博客      百度搜素 爱国呐博客园

#include<iostream>
#include<stack>
#include<queue>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
int pre[1010];
int rank[1010];
int find(int x)//使用递归写find函数,同时有路径压缩
{if(x!=pre[x])pre[x]=find(pre[x]);return pre[x];
}
void merge(int x,int y)
{x = find(x);y = find(y);if(rank[x]<rank[y])//rank为树的高度,这里为按秩合并pre[x] = y;else{pre[y] = x;if(rank[x]==rank[y])rank[x]++;}
}
int main()
{int m,n;while(1){cin>>n;if(n==0) break;cin>>m;for(int i=1; i<=n; i++)//初始化数组{pre[i]=i;rank[i]=0;}int x,y;for(int i=0; i<m; i++){cin>>x>>y;merge(x,y);}int sum=0;for(int i=1; i<=n; i++){if(pre[i]==i)sum++;}cout<<sum-1<<endl;}return 0;
}

下面进入正题  tajian(离线)求最近公共祖先

下面详细介绍一下Tarjan算法的基本思路:

      1.任选一个点为根节点,从根节点开始。

      2.遍历该点u所有子节点v,并标记这些子节点v已被访问过。

      3.若是v还有子节点,返回,否则下一步。

      4.合并v到u上。

      5.寻找与当前点u有询问关系的点v。

      6.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。

详解见博客  https://blog.csdn.net/Akatsuki__Itachi/article/details/81279220

下面代码转自~~上边这位https://blog.csdn.net/Akatsuki__Itachi/article/details/81279173

题目   Nearest Common Ancestors

A rooted tree is a well-known data structure in computer science and engineering. An example is shown below:


In the figure, each node is labeled with an integer from {1, 2,...,16}. Node 8 is the root of the tree. Node x is an ancestor of node y if node x is in the path between the root and node y. For example, node 4 is an ancestor of node 16. Node 10 is also an ancestor of node 16. As a matter of fact, nodes 8, 4, 10, and 16 are the ancestors of node 16. Remember that a node is an ancestor of itself. Nodes 8, 4, 6, and 7 are the ancestors of node 7. A node x is called a common ancestor of two different nodes y and z if node x is an ancestor of node y and an ancestor of node z. Thus, nodes 8 and 4 are the common ancestors of nodes 16 and 7. A node x is called the nearest common ancestor of nodes y and z if x is a common ancestor of y and z and nearest to y and z among their common ancestors. Hence, the nearest common ancestor of nodes 16 and 7 is node 4. Node 4 is nearer to nodes 16 and 7 than node 8 is.

For other examples, the nearest common ancestor of nodes 2 and 3 is node 10, the nearest common ancestor of nodes 6 and 13 is node 8, and the nearest common ancestor of nodes 4 and 12 is node 4. In the last example, if y is an ancestor of z, then the nearest common ancestor of y and z is y.

Write a program that finds the nearest common ancestor of two distinct nodes in a tree.

Input

The input consists of T test cases. The number of test cases (T) is given in the first line of the input file. Each test case starts with a line containing an integer N , the number of nodes in a tree, 2<=N<=10,000. The nodes are labeled with integers 1, 2,..., N. Each of the next N -1 lines contains a pair of integers that represent an edge --the first integer is the parent node of the second integer. Note that a tree with N nodes has exactly N - 1 edges. The last line of each test case contains two distinct integers whose nearest common ancestor is to be computed.

Output

Print exactly one line for each test case. The line should contain the integer that is the nearest common ancestor.

Sample Input

2
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
16 7
5
2 3
3 4
3 1
1 5
3 5

Sample Output

4
3

链式前向星写法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#define eps 1e-8
#define memset(a,v) memset(a,v,sizeof(a))
using namespace std;
typedef long long int LL;
const int MAXL(1e6);
const int INF(0x7f7f7f7f);
const int mod(1e9+7);
int dir[4][2]= {{-1,0},{1,0},{0,1},{0,-1}};
struct node
{int to;int next;
}edge[MAXL+50];
int head[MAXL+50];
int father[MAXL+50];
bool vis[MAXL+50];
bool is_root[MAXL+50];
int n;
int cnt;
int cx,cy;
int ans;
int root;int Find(int x)
{if(x!=father[x])father[x]=Find(father[x]);return father[x];
}void Join(int x,int y)
{int fx=Find(x),fy=Find(y);if(fx!=fy)father[fy]=fx;
}void add_edge(int x,int y)
{edge[cnt].to=y;edge[cnt].next=head[x];head[x]=cnt++;
}void init()
{cnt=0;memset(head,-1);memset(vis,false);memset(is_root,true);scanf("%d",&n);for(int i=0;i<=n;i++)father[i]=i;for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);add_edge(x,y);is_root[y]=false;}for(int i=1;i<=n;i++)if(is_root[i]==true)root=i;
}void LCA(int u)
{for(int i=head[u];~i;i=edge[i].next){int v=edge[i].to;LCA(v);Join(u,v);vis[v]=true;}if(cx==u&&vis[cy]==true)ans=Find(cy);if(cy==u&&vis[cx]==true)ans=Find(cx);
}
void solve()
{scanf("%d%d",&cx,&cy);LCA(root);
}
int main()
{int T;scanf("%d",&T);while(T--){init();solve();cout<<ans<<endl;}
}

上面链接中还有vector模拟邻接表写法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#define eps 1e-8
#define memset(a,v) memset(a,v,sizeof(a))
using namespace std;
typedef long long int LL;
const int MAXL(1e4);
const int INF(0x7f7f7f7f);
const int mod(1e9+7);
int dir[4][2]= {{-1,0},{1,0},{0,1},{0,-1}};
int father[MAXL+50];
bool is_root[MAXL+50];  //记录该店是不是根结点
bool vis[MAXL+50];      //标记该点是否被访问过
vector<int>v[MAXL+50];  //存图
int root;    //为找到的根结点
int cx,cy;   //要查询的两点
int ans;
int Find(int x)
{if(x!=father[x])father[x]=Find(father[x]);return father[x];
}void Join(int x,int y)
{int fx=Find(x),fy=Find(y);if(fx!=fy)father[fy]=fx;
}void LCA(int u)
{for(int i=0; i<v[u].size(); i++)  //对根结点的所有子结点遍历{int child=v[u][i];if(!vis[child]){LCA(child);     //递归查找每一个结点,直到到叶子结点为止Join(u,child);  //回溯的时候更新father用的//例如把题目中的[9]更新为5vis[child]=true;  //访问过的点vis数组为true}}if(u==cx&&vis[cy]==true) //若和u有关系的点被访问过,ans=Find(cy);    //则最近公共祖先为那个有关系点的祖先if(u==cy&&vis[cx]==true)  //若没有被访问过,则不操作ans=Find(cx);}void init()
{memset(is_root,true);memset(vis,false);int n;scanf("%d",&n);for(int i=0; i<=n; i++)v[i].clear();for(int i=1; i<=n; i++)father[i]=i;for(int i=1; i<n; i++){int x,y;scanf("%d%d",&x,&y);v[x].push_back(y);is_root[y]=false;   //y点有入度,所以y点不是根结点}scanf("%d%d",&cx,&cy);  //cx,cy为要查询的两点for(int i=1; i<=n; i++) //找根结点{if(is_root[i]==true){root=i;break;}}}
int main()
{int T;scanf("%d",&T);while(T--){init();      //建树(图),找根结点LCA(root);   cout<<ans<<endl;}
}

LCA(最近公共子序列)相关推荐

  1. 最长公共子序列(LCS)问题 Longest Common Subsequence 与最长公告字串 longest common substr...

    问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字符序列X="x0,x1,-,xm-1",序列Y=& ...

  2. 【动态规划】最长公共子序列与最长公共子串

    1. 问题描述 子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串 cnblogs belong 比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序与 ...

  3. POJ 3080 多个串最长公共子序列

    求多个串最长公共子序列,字典序最小输出.枚举剪枝+kmp.比较简单,我用find直接查找16ms #include<iostream> #include<string> #in ...

  4. java实现最长连续子序列_最长公共子序列 ||

    问题:在 前一篇文章 最长公共子序列 | 的基础上要求将所有的最长公共子序列打印出来,因为最长公共子序列可能不只一种. 难点:输出一个最长公共子序列并不难,难点在于输出所有的最长公共子序列,我们需要在 ...

  5. 动态规划—最长公共子序列问题 HDU-1159 Common Subsequence

    动态规划-最长公共子序列问题 Common Subsequence [ HDU - 1159 ] A subsequence of a given sequence is the given sequ ...

  6. 触类旁通,经典面试题最长公共子序列应该这么答

    作者 |  labuladong 来源 | labuladong(ID:labuladong) [导读]最长公共子序列(Longest Common Subsequence,简称 LCS)是一道非常经 ...

  7. 模板 - 最长上升子序列与最长公共子序列

    整理的算法模板合集: ACM模板 目录 1.最长上升子序列(LIS) 1.1树状数组优化O(nlogn)O(nlogn)O(nlogn) 2.最长公共子序列(LCS) 2.1转换成LIS优化O(nlo ...

  8. 最长公共子序列(LCS)问题算法详解+例题(转换成LIS,优化为O(nlogn),看不懂你来打我)

    目录 最长公共子序列(LCS)问题 1.朴素做法 O(n2)O(n^2)O(n2) 2.转换成LIS优化O(nlogn)O(nlogn)O(nlogn) 3.P2758 编辑距离 最长公共子序列(LC ...

  9. LCS最长公共子序列和LIS最长上升子序列——例题剖析

    一.LCS最长公共子序列 最长公共子序列(LCS)问题算法详解+例题(转换成LIS,优化为O(nlogn),看不懂你来打我) longest comment subsequence 模板题 longe ...

  10. 9.68最长公共子序列

    最长公共子序列 寻找两个字符串中的公共子串,可以不连续 #include<iostream> #include<cstring> using namespace std; ch ...

最新文章

  1. BTN8982基本测试
  2. 每一个程序员都是自学成才?
  3. 视频(avi)转换为图片(Python代码实现)
  4. 手机输入法并一不一定要横向充满
  5. php怎么把字符转成大写,php将字符串全部转换成大写或者小写的方法
  6. 关于Behavior的使用方法
  7. HCIA笔记-----第二天
  8. BestCoder Round #86 HDU 5804,HDU 5805,HDU 5806,HDU 5807
  9. springboot在Gradle7以上版本不识别compile解决方案
  10. MybatisX idea 快速开发插件
  11. 通过Stream流找出集合中对象中某个属性重复的值.
  12. LSNUOJ 1693 神奇的fans
  13. 金浦钛业:抢滩智能制造 凝聚高质发展能量
  14. 【NLP】Praat库(2) Batch processing of files
  15. Win系统下快速批量截图
  16. SLURM 安装与配置
  17. java label 标签_Java标签(Label)
  18. 【GDKOI训练】纸牌游戏(card)
  19. python学习总结(二)——列表
  20. 《计算机辅助教学及应用实践研究》,《论文_浅谈计算机辅助教学(定稿)》

热门文章

  1. redis持久化(persistence)
  2. 剑指offer七:两个链表的第一个公共结点
  3. spring mybatis使用时报 java.lang.AbstractMethodError
  4. 【linux】ubuntu更改mysql数据文件路径
  5. iconv-----linux gbk 转 UTF-8
  6. TCP/IP FTP/TFTP
  7. 从哪儿摔倒,从哪儿爬起
  8. 编写一个基于控制台的聊天室程序
  9. qmail 发邮件故障
  10. The essentiality for Close-Out The Project