LCA 最近公共祖先
LCA ,也就是最近公共祖先是什么意思呢。
下面这张图可能会让你清楚的明白什么是最近公共祖先。
对于初始点,前提是它能构成一棵不成环的树,之所以不能成环,从定义就看出来了嘛,如果成环,是不是有种1是3的父亲,3是6的父亲,6又是1的父亲的感觉。O(∩_∩)O哈哈~
那么我们又从上面一句话能看出另一个问题了,那就是1是6的最远的祖先,3是6最近的祖先是不?
那么这就引出了最近公共祖先的概念了。
那么由上图举几个例子。
13 和11 的最近公共祖先就是6
13和2 的最近公共祖先就是1
8和6的最近公共祖先就是1
5和2的最近公共祖先是2
概念有了,那我们在编程的时候到底怎么去使用呢。
由于笔者也是刚入门,所以只会一种比较普遍的
Tarjin (离线算法)
这是一种比较常见的算法,用到了邻接表这个东西。
学长给我们当时讲是用vector容器作为邻接表。个人感觉很方便。所以在这顺便介绍一下。会的可以直接跳过。
************************************************************************
所谓邻接表,百度百科给出的定义不免显得很抽象,对于有向图,vi的邻接表中每个表结点都对应于以vi为始点射出的一条边。因此,将有向图的邻接表称为出边表。
在上面的那张表我们可以清晰的看出1的邻接点为2、3、4;
6的邻接点为3、10、11。
那么我们怎么来储存这种关系呢。
在这里我们用的是vector 容器来储存。
方式为:vector < int > vec[n] ; //n为节点数,vec[i]就储存了i的邻接点。
如果需要储存两个参数,比如在查询的时候要按顺序输出,那么我们就可以弄个有两个参数的。
方式为:vector< pair <int ,int > > query[n] ;
那么怎么存入元素呢,很显然是 query[x].push_back({y,i}) ;//y是邻接点,i是输入的序号,x是节点
那么怎么用呢,方法为:query[x][ j ].first ;// j是第几个邻接点,这个表示的是y;同理表示i就是 :query[x][ j].second;
用法就到这里结束了。(ps:可能是我太菜了。。。)
*********************************************************************************************************************
那么邻接表有了,接下来怎么做呢,
在这里我们假设题目要你查询3、4节点的最近公共祖先和1、4节点的最近公共祖先!
在这里我们需要用到DFS来进行查找,直到找到根节点。
从上图我们可以发现用dfs查到了3节点。
我们用Q来表示查询是否有它需要查询的节点已经被查找了。
如果有那就直接将那个节点的父节点存起来(这里用的是并查集)。
为什么存父亲节点呢,其实到后面你就发现父亲节点就是最近共祖先!!!
黄色的数字表示该节点的父节点。
我们清晰的看到1、2、3节点原来的父亲节点是指向自己节点本身的,
3节点查询之后发现需要查询的节点(也就是4节点)没有被查找过的,
那么3节点的父亲节点指向2,并且返回2节点
然后2节点继续向邻接点查询,向4节点查询。
4节点父亲节点是4本身,然后查询,是否有需要查询的节点被访问过了,我们发现3节点被访问过了,那么我们将3节点的父亲节点存入结果中。并发现1节点也已经被访问过了,而且父亲节点还是1节点本身,那么我们也把结果存起来。
查询完了之后,将4节点的父亲节点指向2;并且返回2节点;
我们发现没有邻接点可以查了,
然后对2节点查询,发现没有要查询的,于是将2节点的父亲节点指向1,并且返回1节点;
然后对1节点进行查询,发现4节点已经访问过了。
于是我们将4节点的父亲节点覆盖掉我们之前存的答案,
但是我们很明显的发现,通过并查集4的父亲节点这时候已经指向1了,所以覆盖并不影响结果。
最后我们就跳出了dfs,并且我们已经完成了所有的查询。
于是在主函数输出结果就ok,答案应该是
2
1
这就是最近公共祖先的基本求解思路,用到了邻接表,dfs,并查集。
这种方法也是最常用的算法来求最近公共祖先了,具有一定的普适性。
ps:可能是我不会其他方法吧,O(∩_∩)O哈哈~
还有就是上面几张图明显看出纯手画,画的不好,多多包涵哈。
这就是
Tarjin (离线算法)的基本思路了。
如果有不对的地方,望dalao 们在评论区指正,O(∩_∩)O谢谢~
现在放出一道模板题。附上AC代码;
POJ-1330
Nearest Common Ancestors
AC 代码如下:
1 #include<iostream> 2 #include<stdio.h> 3 #include<vector> 4 #include<string.h> 5 #include<algorithm> 6 #define N 100005 7 using namespace std; 8 int vis[N],pre[N],ans[N]; 9 vector <int> vec[N]; 10 vector <pair <int,int> > query[N]; 11 int Find(int x) 12 { 13 if(pre[x]==x) 14 { 15 return x; 16 } 17 else 18 { 19 pre[x]=Find(pre[x]); 20 return pre[x]; 21 } 22 } 23 void dfs(int u,int fa) 24 { 25 pre[u]=u; 26 vis[u]=1; 27 for(int i=0;i<vec[u].size();i++) 28 { 29 int v=vec[u][i]; 30 if(v==fa) continue;//防止走回头路 31 dfs(v,u);//邻接表查询 32 } 33 for(int i=0;i<query[u].size();i++)//查询 34 { 35 int v=query[u][i].first; 36 int id=query[u][i].second; 37 if(vis[v]==1) 38 { 39 ans[id]=Find(v); 40 } 41 } 42 pre[u]=fa;//查询完毕,指向父亲节点。 43 44 } 45 int main() 46 { 47 int T; 48 cin>>T; 49 while(T--) 50 { 51 memset(vis,0,sizeof(vis)); 52 memset(ans,0,sizeof(ans)); 53 memset(pre,0,sizeof(pre)); 54 int n,x,y; 55 cin>>n; 56 for(int i=0;i<n-1;i++) 57 { 58 cin>>x>>y; 59 vec[x].push_back(y); 60 vec[y].push_back(x); 61 vis[y]=1; 62 } 63 int q; 64 q=1; 65 for(int i=0;i<q;i++) 66 { 67 cin>>x>>y; 68 query[x].push_back({y,i}); 69 query[y].push_back({x,i}); 70 } 71 for(int i=1;i<=n;i++) 72 { 73 if(vis[i]==0) 74 { 75 memset(vis,0,sizeof(vis)); 76 dfs(i,i); 77 break; 78 } 79 } 80 for(int i=0;i<q;i++) 81 { 82 cout<<ans[i]<<endl; 83 } 84 for(int i=1;i<=n;i++) 85 { 86 vec[i].clear(); 87 query[i].clear(); 88 } 89 } 90 return 0; 91 }
转载于:https://www.cnblogs.com/ISGuXing/p/7219889.html
LCA 最近公共祖先相关推荐
- LCA(最近公共祖先)问题
1.这个算法基于并查集和深度优先搜索.算法从根开始,对每一棵子树进行深度优先搜索,访问根时,将创建由根结点构建的集合,然后对以他的孩子结点为根的子树进行搜索,使对于 u, v 属于其某一棵子树的 LC ...
- Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)...
转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2194090a96bbed2db1351de8.html 基本概念: 1.割点:若删掉某点后,原连通图 ...
- LCA 最近公共祖先(RMQ、树上倍增、Tarjan),树上两点距离,线段重合长度
对于LCA的一些理解 RMQ dfs处理树 对于一个树形结构,可以用dfs将一颗树转化成数组,数组中记录每个点的标号,这样数组就按照dfs的顺序把树存了下来 确定祖先的范围 对于询问的节点X和Y, X ...
- POJ 1330 LCA最近公共祖先 离线tarjan算法
题意要求一棵树上,两个点的最近公共祖先 即LCA 现学了一下LCA-Tarjan算法,还挺好理解的,这是个离线的算法,先把询问存贮起来,在一遍dfs过程中,找到了对应的询问点,即可输出 原理用了并查集 ...
- LCA(最近公共祖先)(leetcode 236 python C++)
LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. # Definition for a binary tree node. ...
- 故事篇之 LCA 最近公共祖先(一)
故事出真知 阿珍 爱上了 阿强 但 被他们的父母 拒绝 了 没错 狗血的近亲结婚剧情开始了 阿珍 说: 不行,我深深爱着我的阿强,谁也不能把我们分开 阿强 深情地望着阿珍 说 一定会有机会的! 此时 ...
- 模板 - LCA最近公共祖先(倍增法、Tarjan、树上差分、LCA优化的次小生成树)
整理的算法模板合集: ACM模板 注意x和y的LCA可以是x或者y本身 一.LCA的在线倍增算法 /*给定一棵包含 n个节点的有根无向树,有 m个询问,每个询问 给出了一对节点的编号 x和 y,询问 ...
- LCA 最近公共祖先 详解
一.内容 给定一棵包含 n 个节点的有根无向树,节点编号互不相同,但不一定是 1∼n.有 m 个询问,每个询问给出了一对节点的编号 x 和 y,询问 x 与 y 的祖孙关系.输入格式 输入第一行包括一 ...
- LeetCode Lowest Common Ancestor of a Binary Search Tree (LCA最近公共祖先)
题意: 给一棵二叉排序树,找p和q的LCA. 思路: 给的是BST(无相同节点),那么每个节点肯定大于左子树中的最大,小于右子树种的最小.根据这个特性,找LCA就简单多了. 分三种情况: (1)p和q ...
最新文章
- 在 Linux 上给用户赋予指定目录的读写权限
- 你绝对能懂的“机器学习”(四)
- android studio自动注释
- 学车比学计算机简单,帅哥自述:学车比上班还要累
- 1.3. kermit
- CCP(Cost complexity pruning) on sklearn with python implemention
- Mysql数据库使用总结
- python的urllib四大模块_Python常用的内建模块4:urllib
- 有关PHP、HTML单引号、双引号转义以及转成HTML实体的那些事!
- JSON(5)---JSON.stringify()
- SVN汉化失败的原因及解决方案(通过SVN官网下载SVN和相应的汉化包)
- python数字信号处理pdf_数字信号处理
- 消消乐游戏原理(附部分代码)
- 超详细的Latex快速基础入门(第一节)
- HDU 2201 熊猫阿波问题==金刚坐飞机问题
- Linux之常见面试题知识点批注(七)
- 蹩脚的Access分页语句
- html+css主页页眉实现
- php html 纯文本,php – 使用纯文本回退发送HTML简报
- LVTTL与LVCMOS区别
热门文章
- 浅谈游戏程序设计入门
- 数据结构中符号的作用(图解)
- 第三方网站用户和微信用户的绑定方法
- wordpress禁用Gutenberg(古藤保)编辑器代码
- 股票配资平台安全可靠吗?一文告诉你!
- python声明一个数组_python 声明数组
- 阿斯利康英飞凡在华获批用于治疗广泛期小细胞肺癌;​君实生物与嘉晨西海成立合资公司 | 医药健闻...
- 30个农村创业项目_6个适合百姓的冷门创业项目,乡村乡镇,小本生意却受农村人欢迎...
- [2019编程]第六节教学大纲:学生类Student的基本概念及初步应用/CSS选择器的种类
- Windows 7 升级顾问