LCA,最近公共祖先,实现有多种不同的方法,对于树上的问题有着广泛的应用,比如树上的最短路径。

常用的解决LCA问题的算法有:Tarjan算法,Doubly/倍增算法,转化为RMQ问题等。

本文介绍基于DFS+二分搜索的在线算法,Doubly/倍增算法。(此处二分不同于二分查找算法那种)

算法初探:

对于已知的一棵树(已编号),若记录点v到根的深度为depth[v]。那么,如果节点w是u和v的祖先的话,让v向上走depth[v] - depth[w]步,u向上走depth[u] - depth[w]步,就都会走到w。因此,首先让u个v中深度较深的先向上走|depth[u] - depth[v]|步,再一步一步向上走,直到走到同一节点,就可以在O(depth[u] + depth[v])时间内求出LCA。

主要代码:

//输入
vector<int> tree[MAX_V];//vector存树
int root;//根节点
int parent[MAX_V];//父亲节点
int depth[MAX_V];//节点的深度
void dfs(int v, int fa) {parent[v] = fa;for (int i = 0; i < tree[v].size(); i++) {if (tree[v][i] != fa) {depth[tree[v][i]] = depth[v] + 1;dfs(tree[v][i], v);}}
}
//预处理
void init() {depth[root] = 0;dfs(root, -1);
}
//计算u和v的LCA
int lca(int u, int v) {//先让u和v先走到同一深度while (depth[u] > depth[v]) u = parent[u];while (depth[v] > depth[u]) v = parent[v];//让u和v走到同一深度while (u != v) {u = parent[u];v = parent[v];}return u;
}

算法改进->倍增算法

由于节点最大深度为N,此思路对于Q次查询的复杂度为O(Q*N),如果Q相对于N较大,此算法最少会达到O(N^2)级别,算法有没有可以改进的地方呢?

在节点向上走的过程,是一步一步走的,我们可以由此改进算法,减少走的次数。

对于每个节点,记录其往上走1个节点,2个节点,4个节点,8个节点,16个节点……2^k个节点所达到的节点编号,具体为利用节点其父亲节点信息,可以通过parent2[v] = parent[parent[v]]得到其向上走两步的节点,再通过这一信息,得到parent4[v] = parent2[parent2[v]],即其向上走四步的节点,以此类推......记录节点向上走2^k步的节点为parent[k][v]。有了k = (int)floor(logN / log(2.0))以内的所有信息,就可以二分搜索了。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
int n;//节点数量 0 ~ n - 1编号
int cnt, head[100005];//前向星存树
int depth[100005];//深度
int parent[20][100005];//parent[i][v]表示v节点往上走2^i步到达的顶点
struct node {int to, next;
}edge[200010];
void add(int x, int y) {//树加边cnt++;edge[cnt].to = y;edge[cnt].next = head[x];head[x] = cnt;
}
void dfs(int x, int fa) {//DFS,搜索得每个点的深度  O(n)parent[0][x] = fa;for (int i = head[x]; i; i = edge[i].next) {int y = edge[i].to;if (y == fa) continue;depth[y] = depth[x] + 1;dfs(y, x);}
}
void init() {//预处理,打2^k表  O(nlogn)memset(depth, 0, sizeof(depth));memset(parent, -1, sizeof(parent));dfs(0, -1);//以0为根节点  预处理出parent[0]和depthint k = (int)floor(log(n) / log(2.0));for (int i = 0; i + 1 <= k; i++) {for (int j = 0; j < n; j++) {if (parent[i][j] == -1) parent[i + 1][j] = -1;else parent[i + 1][j] = parent[i][parent[i][j]];//倍增思想}}
}
int lca(int x, int y) {//LCA  O(logn)if (depth[x] > depth[y]) swap(x, y);int k = (int)floor(log(n) / log(2.0));for (int i = 0; i <= k; i++) {//让x,y走到同一高度if ((depth[y] - depth[x]) >> i & 1) {y = parent[i][y];}}if (x == y) return x;for (int i = k; i >= 0; i--) {//二分搜索 二分搜索指的是下一个数值对应的步数是现在的一半,即二分了if (parent[i][x] != parent[i][y]) {x = parent[i][x];y = parent[i][y];}}return parent[0][x];
}
int main() {int q;int x, y;while(scanf("%d%d%d",&n,&q)!=EOF) {cnt = 0;memset(head, 0, sizeof(head));for (int i = 1; i < n; i++) {//n - 1条边scanf("%d%d", &x, &y);add(x, y);add(y, x);}init();for (int i = 0; i < q; i++) {scanf("%d%d", &x, &y);printf("%d\n", lca(x, y));}}return 0;
}

由代码实现可知,改进后的算法,即Doubly算法的复杂度为O(nlogn + Q*logn),Q为查询次数。
注:这里的2^i和二进制位密切相关,存储向上走2^i步到达的节点不是凭空而来的,目的就是之后走的步数数值的二进制位与这个相关联,相当于把数值的每个二进制位分离出来处理,减少计算量,就像快速幂计算。

三种LCA算法(一):Doubly算法(倍增算法)相关推荐

  1. LVS三种模式的区别及负载均衡算法

    转载自  LVS三种模式的区别及负载均衡算法 LVS简介 LVS(Linux Virtual Server)即Linux虚拟服务器,是一个虚拟的服务器集群系统,由章文嵩博士在1998年5月成立,在li ...

  2. C语言基本数据结构之二(二叉树的三种遍历,节点数以及深度算法)

    关于二叉树的定义,网上有比较好的介绍,在这里就简单介绍二叉树的一些性质 二叉树的基本性质 1)二叉树的第i层上至多有 2^(i-1)(i ≥1)个结点: 2)深度为 h 的二叉树中至多含有 2^h – ...

  3. 三种基于稀疏成像的RCS测量算法思想

    1.基于三维稀疏成像的RCS测量方法 算法思路: 1.先设置初始参数: 2.原始回波数据进行脉冲压缩,得到距离向矩阵: 3.对距离向回波数据(脉冲压缩后的数据)进行K倍频域升采样: 3.1 提取距离向 ...

  4. 计算机网络二分法划分网络,三种经典复杂网络社区结构划分算法研究_GN算法

    论文导读::复杂网络是复杂系统的高度抽象.即社区结构特性[3].算法是一种试探优化法[4].算法. 关键词:复杂网络,社区结构,Laplace图谱,Kernighan-Lin算法,GN算法 1引言 现 ...

  5. 冒泡排序,插入排序,选择排序三种算法的优劣

    https://www.jianshu.com/p/9f724c880124 最近听了王争老师的数据结构与算法之美,大有获益,特写此博客与大家分享. 排序算法太多了,但大体可以归结于三类,冒泡排序,插 ...

  6. Redis 实现限流的三种方式

    欢迎关注方志朋的博客,回复"666"获面试宝典 面对越来越多的高并发场景,限流显示的尤为重要. 当然,限流有许多种实现的方式,Redis具有很强大的功能,我用Redis实践了三种的 ...

  7. matlab提取电压基波分量,有源电力滤波器三种基波提取方法的对比分析

    随着现代电力电子技术的飞速发展,电网中增加了大量的非线性负载,如大容量变流设备.变频设备.开关电源等的广泛应用,导致大量谐波的产生,这些谐波使电网电压和电流波形发生畸变,使得电能质量日益下降. 有源电 ...

  8. 快速排序的三种实现以及应用场景

    好了,废话不多说,直接上干货. 1.快速排序的概念: 快速排序(Quicksort)是对冒泡排序的一种改进. 快速排序由C. A. R. Horno在1962年提出.它的基本思想是:通过一趟排序将要排 ...

  9. 3D目标检测算法调研FCOS/FCOS3D/FCOS3D++算法介绍

    一.综述 3D检测背景 二维目标检测算法能识别物体的类别.平面坐标以及边长,是计算机视觉中的一个基本问题.但是对于自动驾驶来说,二维信息还不足以让汽车充分感知三维立体的真实世界,当一辆智能汽车需要在道 ...

  10. 协同过滤推荐算法和基于内容推荐算法的区别?

    文章最前: 我是Octopus,这个名字来源于我的中文名--章鱼:我热爱编程.热爱算法.热爱开源.所有源码在我的个人github :这博客是记录我学习的点点滴滴,如果您对 Python.Java.AI ...

最新文章

  1. 8s 接口压力测试_Python Locust 基于Robot Framework实现关键字驱动接口性能测试
  2. java实现高斯赛德尔算法解线性方程组
  3. jQuery 属性操作——案例:购物车案例模块
  4. Vue+Leaflet实现加载OSM显示地图
  5. 需求用例分析之八:用例颗粒度
  6. 【mongoDB】测试使用gridfs,配置一个分片服务器集群
  7. Windows 7 任务栏开发 之 进度条(Progress Bar)
  8. JAVA实现可视化的xpath_【Java】爬虫基础 -- XPath 小案例
  9. 联想笔记本键盘排线_三分钟了解笔记本键盘维修技巧
  10. WPF自定义窗体仿新毒霸关闭特效(只能在自定义窗体中正常使用)
  11. Jquery向页面append新元素之后,如何解决事件的绑定问题?
  12. 面试必备:零拷贝详解
  13. 【Android】实现自定义标题栏
  14. 2020.10.20读 PointNet++: Deep Hierarchical Feature Learning on Point Sets in a Metric Space
  15. 手写英文单词识别(1)
  16. win8.1 终于删除可恶的微软拼音
  17. “一文读懂”系列:AMS是如何动态管理进程的?
  18. 【task09】集合运算---内连结
  19. 【智能制造】工信部:《国家智能制造标准体系建设指南(2018年版)》(征求意见稿)
  20. 【linux学习笔记】嵌入式linux学习笔记

热门文章

  1. UINO优锘:DMV架构管理可视化,让架构图管理快速升级
  2. 计算机平面设计专业可以考什么证,平面设计师资格证怎么考_计算机平面设计职称...
  3. 我看肖老师的《明日世界-云端计算下的程序设计需求》视频时,做的简单笔记
  4. ERROR: Cannot uninstall ‘PyYAML‘. It is a distutils installed project and thus we cannot accurately
  5. MySQL高级索引及调优篇
  6. 2021厦大计算机考研炸了,厦门大学2021年硕士研究生复试名单
  7. mkv文件用什么软件打开_什么是MKV文件,以及如何播放它们?
  8. dna计算机的主要作用,我国首台DNA计算机 意义重大
  9. 姐妹素数java_Java之姐妹素数
  10. 如何在win10上显示隐藏文件