LCA(最近公共祖先)求解方法
本文参考https://oi-wiki.org/graph/lca/
定义树上u,v两点的LCA(最近公共祖先)是从根节点dfs到上述两节点路径上距离上述两点最近的公共点。
LCA有如下性质:
1、u是v的祖先,当且仅当LCA(u,v)=u
2、d(u,v)=h(u)+h(v)-2h(LCA(U,V))。其中d为树上两点距离,h为某点到树根的距离。
LCA求解方法如下:
1、朴素算法:每次寻找深度较大节点控制其上跳,最终实现重合。这里需要预处理整棵树,获得深度数组。
代码如下:
1 #include <iostream>2 using namespace std;3 int h[100];//深度存储4 int child[100];5 int sib[100];6 int parent[100];7 void insert(int p,int self){8 int p_c = child[p];9 child[p]=self;
10 sib[self]=p_c;
11 parent[self]=p;
12 }//左孩子右兄弟存储
13 int l=1;
14 void dfs(int u){
15 h[u]=l;
16 for (int i=child[u];i;i=sib[i]){
17 l++;
18 dfs(i);
19 l--;
20 }
21 }//dfs遍历获取深度
22 int find(int u,int v){
23 int l_1 = h[u];
24 int l_2 = h[v];
25 while(u!=v){
26 if (l_1>l_2){
27 l_1--;
28 u=parent[u];
29 }
30 else if(l_1<l_2){
31 l_2--;
32 v=parent[v];
33 }
34 else{
35 u=parent[u];
36 v=parent[v];
37 }
38 }//当位于相同高度时就一起跳
39 return u;
40 }
41 int main(){
42 insert(4,2);
43 insert(4,1);
44 insert(1,3);
45 insert(1,5);
46 dfs(4);//4是根节点
47 cout<<find(3,2);
48 return 0;
49 }
2、倍增算法:对上述朴素算法的优化,把跳跃过程二进制化来减少跳跃次数。
示例代码如下:
1 #include <iostream>2 #include <algorithm>3 using namespace std;4 int fa[100][100];5 int child[100];6 int sib[100];7 int h[100];8 void insert(int p,int self){9 int p_c = child[p];
10 child[p]=self;
11 sib[self]=p_c;
12 }//左孩子右兄弟存储
13 void dfs(int u,int l){
14 h[u]=l;//存储深度
15 for (int i=1;i<=20;i++)
16 fa[u][i]=fa[fa[u][i-1]][i-1];
17 for (int i=child[u];i;i=sib[i]){
18 fa[i][0]=u;
19 dfs(i,l+1);
20 }
21 }
22 int find (int u,int v){
23 int l_1 = h[u];
24 int l_2 = h[v];
25 if (l_1<l_2)
26 swap(u,v);//保证u的深度大于等于v
27 for (int i=20;i>=0;i--){
28 if (h[fa[u][i]]>=h[v])//如果可以缩小深度就跳跃。同时为了简便我们选择从大到小跳
29 u=fa[u][i];
30 if (u==v)
31 return u;
32 }
33 for (int i=20;i>=0;i--){
34 if (fa[u][i]!=fa[v][i]){//相同深度后一起倍增跳
35 u=fa[u][i];
36 v=fa[v][i];
37 }
38 }
39 return fa[u][0];
40 }
41 int main(){
42 insert(4,2);
43 insert(4,1);
44 insert(1,3);
45 insert(1,5);
46 dfs(4,1);//4是根节点
47 cout<<find(1,5);
48 return 0;
49 }
3、Tarjan算法(求解LCA):这个算法作为离线算法,其根源还是利用dfs进行遍历,不同的是其利用并查集实现了信息的存储。
其大致思路如下:
此处以二叉形式为例,圆圈为根节点,三角为根节点下属子树。这里运用数学归纳法实现思路推导:假设tarjan(n)可以解决以n为根的树的LCA求解问题,于是我们分别对左右子树使用tarjan算法,并将完成tarjan算法遍历的子树通过并查集与根节点连接,如下图所示:
这里红色表示已经对子树使用tarjan算法,并且将子树与根通过并查集连接到了一起。这时,我们再对右子树重复上述过程。得到了左右子树都解决LCA问题的一个树。这个树可以实现两查找节点都在左子树或者都在右子树的LCA查找。现在考虑查找节点分别位于左右节点的情况。因为通过并查集将左右子树连接在一起,所以最终只需寻找其祖宗节点即可。
为了防止无法寻找到真正的LCA,这里采用visit数组来记录节点的访问情况。如果两节点其中一个未被访问,就证明我们还未获取另一节点的相对位置信息,暂时不需要对其使用tarjan算法。只有当两节点都被访问后我们才可以根据二者的相对位置信息来使用tarjan算法。
示例代码如下:
1 #include <iostream>2 #include <cstring>3 using namespace std;4 typedef struct ask{5 int u,v;6 int ans;7 }ask;8 ask a[100];//询问 9 int parent[100];
10 int child[100];
11 int sib[100];
12 int visit[100];
13 void insert(int p,int s){
14 int p_c = child[p];
15 child[p]=s;
16 sib[s]=p_c;
17 }
18 void init(){
19 for (int i=0;i<100;i++)
20 parent[i]=i;
21 }
22 int find_root(int u){
23 if (parent[u]==u)
24 return u;
25 else
26 return find_root(parent[u]);
27 }
28 void Union(int u,int v){
29 int p_u = find_root(u);
30 int p_v = find_root(v);
31 parent[p_v]=p_u;//顺序不能反,保证是后面的以前面的为祖先节点
32 }
33 void tarjan(int u){
34 visit[u]=1;
35 for (int i=child[u];i;i=sib[i]){
36 tarjan(i);
37 Union(u,i);
38 }
39 int v_1 = a[0].u;
40 int v_2 = a[0].v;
41 if (visit[v_1]&&visit[v_2])
42 a[0].ans=find_root(v_1);
43 }
44 int main(){
45 init();
46 insert(1,2);
47 insert(1,3);
48 insert(2,4);
49 insert(2,5);
50 insert(3,6);
51 a[0].u=4;
52 a[0].v=3;
53 tarjan(1);
54 cout<<a[0].ans;
55 return 0;
56 }
LCA(最近公共祖先)求解方法相关推荐
- LCA(最近公共祖先)问题
1.这个算法基于并查集和深度优先搜索.算法从根开始,对每一棵子树进行深度优先搜索,访问根时,将创建由根结点构建的集合,然后对以他的孩子结点为根的子树进行搜索,使对于 u, v 属于其某一棵子树的 LC ...
- LCA 最近公共祖先
LCA ,也就是最近公共祖先是什么意思呢. 下面这张图可能会让你清楚的明白什么是最近公共祖先. 对于初始点,前提是它能构成一棵不成环的树,之所以不能成环,从定义就看出来了嘛,如果成环,是不是有种1是3 ...
- 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 最近公共祖先 详解
一.内容 给定一棵包含 n 个节点的有根无向树,节点编号互不相同,但不一定是 1∼n.有 m 个询问,每个询问给出了一对节点的编号 x 和 y,询问 x 与 y 的祖孙关系.输入格式 输入第一行包括一 ...
- 模板 - LCA最近公共祖先(倍增法、Tarjan、树上差分、LCA优化的次小生成树)
整理的算法模板合集: ACM模板 注意x和y的LCA可以是x或者y本身 一.LCA的在线倍增算法 /*给定一棵包含 n个节点的有根无向树,有 m个询问,每个询问 给出了一对节点的编号 x和 y,询问 ...
- LeetCode Lowest Common Ancestor of a Binary Search Tree (LCA最近公共祖先)
题意: 给一棵二叉排序树,找p和q的LCA. 思路: 给的是BST(无相同节点),那么每个节点肯定大于左子树中的最大,小于右子树种的最小.根据这个特性,找LCA就简单多了. 分三种情况: (1)p和q ...
最新文章
- 127.0.0.1和0.0.0.0和localhost的区别
- 一次线上内存报警的研究
- 鸿蒙os 2.0跑分,预装鸿蒙OS 2.0!华为MatePad Pro2跑分曝光:麒麟9000、8GB内存
- “许巍日”新歌提前曝光 《爱如少年》10/15温暖登场!
- 2015-03-18 current note update logic in my task
- 多服务器 elk 搭建 [elasticsearch 7.0 ]
- matlab频谱分析_罗德与施瓦茨两款新的信号和频谱分析仪 具有多种频率型号
- Oracle Sql语句定时执行
- JS小技巧 ----- 关于 ... 运算符的使用场景
- python程序运行原理
- ldap 统一认证 java_LDAP统一认证
- matlab版本下载地址
- 火车头采集html5游戏,火车头采集网站内页URL(图文)教程!
- java String 转map、list
- 电路基础第5版读书笔记(第一章基本概念)
- android studio 使用lint工具优化app时全过程记录
- 程序人生-hello`s P2P
- matlab 单边频率谱,matlab求单边功率谱
- 最值得推荐的6个物联网开发平台
- python读取文件夹下所有图片
热门文章
- wordpress中强大的query posts 用法
- 由世纪互联运营的Microsoft Teams携创新功能正式发布,夯实“企业数字中枢”
- EndNote小技巧:如何方便的显示Research Note
- 支持向量机SVM原理解析
- springboot(三)整合多数据源
- ubuntu安装使用锐捷认证上网
- ArcGIS 10.2 安装教程(含安装失败解决方案)
- 树莓派搭建nginx+rtmp服务器
- JavaScript Date() setDate() 时间的格式化 加一天 减一天
- uni-app购物车页面开发