最近公共祖先结点--LCA(倍增法,言简意赅)

2024-03-13 21:52

本文主要是介绍最近公共祖先结点--LCA(倍增法,言简意赅),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

朴素求法

倍增法


首先我们来想想朴素做法是怎样的。

朴素求法

求两结点的最近公共祖先朴素步骤:

一、先将两结点置于树的同一深度/高度。

(处在下面的结点一步一步往上走,直到与另一个结点处于共同深度)

二、两结点同时向上走,直到遇见同一个结点,则该结点为最近公共祖先。

#include<bits/stdc++.h>
using namespace std;
const int N=100005;int dep[N];//结点的深度
int f[N];//结点的父节点//计算a,b的最近公共祖先
int lca(int a,int b){if(dep[a]<dep[b])swap(a,b);//a点向上爬,直到与b的深度相等while(dep[a]!=dep[b])a=f[a];//两结点一起向上爬,直到父节点为同一个结点while(f[a]!=f[b])a=f[a],b=f[b];//父节点即为最近公共祖先结点return f[a];
}

时间复杂度为:O(n)。

可以发现如果树的深度非常大的话,每次求两结点的lca最差的情况需要把整棵树都重新遍历一遍。这样速度太慢了。

于是有了倍增算法。

倍增法

时间复杂度:O(logn)

反正终点是确定的,为什么我们一次只向上走一个结点呢?能不能一次多走几个结点?

怎么走算多?一次走2^k步算不算多?(有点像二进制优化,一个数肯定可以化成若干个2^k之和,这样我们只需要最多logn步就能到达终点了)

那你怎么知道我走了2^k步后我飘到哪去了?

这个简单,我们只需要预处理出该节点走2^k步后的祖先结点就知道你飘到哪个祖先那去了。

预处理祖先结点。

我们用f[u][k]表示u结点的走2^k步后到达的结点。

于是f[u][0]也就是u走2^0,也就是走一步,也就是到达了u的父节点。于是f[u][0]等于他的父节点。

f[u][1]也就是u走2^1步,也就是走两步,可以看做是先走2^0步到达了u的父结点,然后再走2^0步到达了父节点的父节点。

f[u][i]也就是u走2^i步,我们可以看作是先走2^(i-1)步到达f[u][i-1]结点,然后再走2^(i-1)步。

这样我们就得到了祖先结点的递推公式:

f[u][i]=f[ f[u][i-1] ][i-1];

vector<int>son[N];
int deep[N];
int f[N][30];//f[i][j]记录i结点的第2^j个祖先结点。//预处理结点深度,祖先节点。
void dfs(int u,int p){//当前结点,父节点deep[u]=deep[p]+1;f[u][0]=p;for(int i=1;(1<<i)<=deep[u];i++){f[u][i]=f[f[u][i-1]][i-1];}for(auto it:son[u]){if(it!=p){dfs(it,u);}}
}
//计算x,y的lca
int lca(int a, int b) {if (dep[a] < dep[b]) swap(a, b);//找到同一深度的结点for (int i = 30; i >= 0; i --) {//每次尽量走最多步,所以倒序枚举if (dep[f[a][i]] >= dep[b]) a = f[a][i];    //能走即走if (a == b) return a;}//同时向上走,同样倍增着走,但是不能走到同一个祖先结点上,这样才是最近的。for (int i = 30; i >= 0; i --) {if (f[a][i] != f[b][i]) a = f[a][i], b = f[b][i];}return f[a][0];
}

这篇关于最近公共祖先结点--LCA(倍增法,言简意赅)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/806317

相关文章

SpringBoot自定义注解如何解决公共字段填充问题

《SpringBoot自定义注解如何解决公共字段填充问题》本文介绍了在系统开发中,如何使用AOP切面编程实现公共字段自动填充的功能,从而简化代码,通过自定义注解和切面类,可以统一处理创建时间和修改时间... 目录1.1 问题分析1.2 实现思路1.3 代码开发1.3.1 步骤一1.3.2 步骤二1.3.3

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

poj1330(LCA最近公共祖先)

题意:求最近公共祖先 思路:之前学习了树链剖分,然后我就用树链剖分的一小部分知识就可以解这个题目了,记录每个结点的fa和depth。然后查找时,每次将depth大的结点往上走直到x = y。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstring>

最近心情有点复杂:论心态

一月一次的彷徨又占据了整个身心;彷徨源至不自信;而不自信则是感觉自己的价值没有很好的实现亦或者说是自己不认可自己的目前的生活和状态吧。 我始终相信一句话:任何人的生活形态完全是由自己决定的;外在的总归不能直达一个人的内心深处。所以少年 为了自己想要的生活 多坚持努力吧、不为别人只为自己心中的那一丝执着。 由此我看到了一个故事: 一个心情烦躁的人去拜访禅师。他问禅师:我这辈子就这么注定了吗?您

在二叉树中找到两个节点的最近公共祖先(基于Java)

如题  题解 public int lowestCommonAncestor(TreeNode root, int o1, int o2) {//记录遍历到的每个节点的父节点。Map<Integer, Integer> parent = new HashMap<>();Queue<TreeNode> queue = new LinkedList<>();parent.put(roo

带头结点的线性链表的基本操作

持续了好久,终于有了这篇博客,链表的操作需要借助图像模型进行反复学习,这里尽可能的整理并记录下自己的思考,以备后面复习,和大家分享。需要说明的是,我们从实际应用角度出发重新定义了线性表。 一. 定义 从上一篇文章可以看到,由于链表在空间的合理利用上和插入、删除时不需要移动等优点,因此在很多场合下,它是线性表的首选存储结构。然而,它也存在某些实现的缺点,如求线性表的长度时不如顺序存储结构的

【UVA】10066-The Twin Towers(最长公共子串问题)

赤裸裸的最长公共子串问题,没什么好说的,注意的是,每组数据后面都有一个空行。 13996019 10066 The Twin Towers Accepted C++ 0.015 2014-08-06 00:34:53 #include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using

【hdu】I Hate It(线段树,结点修改求区间最大值)

线段树的模板题,还是二分递归。 #include <iostream>#include <cstdlib>#include <cstdio>#include <string>#include <cstring>#include <cmath>#include <vector>#include <queue>#include <set>#include <map>#incl

【hdu】敌兵布阵(线段树,更加结点,区间求和)

最近开始刷线段树,主要围绕notonlysuccess的线段树总结刷。 结点修改还是比较简单的,不需要什么懒惰标记,直接二分递归就可以了。 #include <iostream>#include <cstdlib>#include <cstdio>#include <string>#include <cstring>#include <cmath>#include <vecto

如何限制与管控员工上网行为?四个方法让员工效率倍增!【企业员工上网行为管理】

在信息化时代,员工的上网行为直接影响着工作效率和企业的安全性。不当的网络使用,如浏览与工作无关的网站、下载不安全的文件,可能导致工作效率低下,甚至引发安全风险。因此,许多企业正在积极寻找有效的措施来管控员工的上网行为,以确保工作效率的提升。 以下是四个常见且有效的员工上网行为管理方法,帮助企业实现更高效的网络管理。 方法一:配置网络防火墙进行访问限制 最基础的员工上网行为管理方法是通过配置防