倍增法求LCA(最近公共最先)

本文详细介绍了在有根树中使用倍增法高效求解两个节点的最近公共祖先(LCA)的方法。通过预处理fa数组存储节点的2^i个祖先,实现对数级别的查询效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 对于有根树T的两个结点u、v,最近公共祖先x=LCA(u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。

 

 

如图,根据定义可以看出14和15的最近公共祖先是10,   15和16的最近公共祖先是1,     6和5的最近公共祖先是5......

假如我要求14和16的最近公共祖先,要怎么做呢?

最暴力的做法,就是先看14和16在不在同一层,如果他们不在同一层,那么较深的那个点往上爬(即距离根较远的那个点)

一直爬,爬到两点的深度一样

当两点深度一样时,判断他们是否在同一个点上,如果不是,则两个点同时往上爬,直到这两个点是同一个点

显然,这么做一般来说都是不行的。

下面介绍倍增法(图是用vector存的)

既然一步一步往上爬太慢了,那就一次爬远一点

我们预处理一个数组fa【x】【i】使游标快速移动,大幅度减少跳转次数。

fa【x】【i】表示 点x的第2^i个祖先

拿上图来说,fa【15】【0】就是点15的第2^0个祖先,即12(fa【15】【0】=12)

      fa【15】【1】就是点15的第2^1个祖先,即10(fa【15】【1】=10)

         fa【15】【2】就是点15的第2^2个祖先,即5(fa【15】【2】=5)

怎么预处理出这个数组呢?我们观察上图,15的第一个祖先是12,12的第一个祖先是10

即fa【15】【0】=12     fa【12】【0】=10

而15的第二个祖先是10,和12的第一个祖先是一样的

fa【15】【1】=10

也就是说,对于点15来说,15的第二个祖先就是15的第一个祖先的第一个祖先(点12的第一个祖先)

用数组表示是fa[15][1]=fa[ fa[15][0] ][ 0 ];

fa[15][1]=fa[12][0]

推广到第2^i个祖先:递推式:fa[rt][i]=fa[fa[rt][i-1]][i-1];(rt为当前节点)

预处理代码:(depth数组存的是当前节点的深度)

void dfs(int f,int rt )//rt是当前节点,f是当前节点的父亲
{
    depth[rt]=depth[f]+1;
    fa[rt][0]=f;
    for(int i=1;i<20;++i)//自己估计i的范围
    fa[rt][i]=fa[fa[rt][i-1]][i-1];
    for(int i=0;i<E[rt].size();++i)//用vector存的图,遍历当前节点的每一个孩子
    dfs(rt,E[rt][i]);
}

下一个问题是,游标移动到哪里合适?

我们的原则是,先让两个点的深度一致

然后,让两个点同时跳相同的步数

1.如果跳出根以外了,就不跳

2.如果跳到的点是相同的点,也不跳(不一定是最近的公共祖先)

不是以上两种情况,就跳

举个栗子,如图:

 

假如我们问14和15的lca是哪个点

按照规则

我们先让14和15在同一层上(深度一致)

14跳到了13

然后,我们看跳多少步合适,如果i=3,即2^3=8步,发现已经跳出根以外了,不跳

再看i=2, 2^2=4,13和15的第四个祖先都是5,不跳

再看i=1,即跳2步,发现都是10,不跳

再看i=0,跳一步,13跳一步到11,15跳一步到12,两个点不相同,可以跳

此时第一遍循环结束,判断这两个点的第一个祖先是不是同一个点

如果是,则找到了

如果不是,那就继续进行循环

用图表示:

第一步是14跳到13

第二步是13跳到11,15跳到12

发现11和12的第一个祖先都是10,找到答案,结束循环 (时间是log级别的)

代码:

int lca(int x,int y)//找x和y的最近公共祖先
{
    if(depth[x]<depth[y])//调整x的深度大一些
        swap(x,y);
    while(depth[x]!=depth[y])//使x和y的深度一致
    {
        for(int i=9;i>=0;--i){//初始常数自己根据问题进行调整
            if(depth[x]-(1<<i)>=depth[y])
                x=fa[x][i];
        }    
    }
    if(x==y) return x;
    while(fa[x][0]!=fa[y][0])//当x和y的第一位祖先都一样时退出循环
    {
        for(int i=9;i>=0;--i){
        //如果没有出界而且两点的祖先不一样,就跳
                if(fa[x][i]!=0&&fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
        }        
    }
    return fa[x][0];
}        
View Code

代码自己写的,有点丑,网上的没看懂....

下面给出测试代码和样例,大家可以去试一试,样例就是第一幅图

#include <iostream>
#include <queue>
#include <vector>
#include <stdio.h>
using namespace std;
vector<int> E[20]; 
int depth[20];
int fa[20][20];
void dfs(int f,int rt )
{
    depth[rt]=depth[f]+1;
    fa[rt][0]=f;
    for(int i=1;i<20;++i)
    fa[rt][i]=fa[fa[rt][i-1]][i-1];
    for(int i=0;i<E[rt].size();++i)
    dfs(rt,E[rt][i]);
}
int lca(int x,int y)
{
    if(depth[x]<depth[y])//调整左边深 
        swap(x,y);
    while(depth[x]!=depth[y])
    {
        for(int i=9;i>=0;--i){
            if(depth[x]-(1<<i)>=depth[y])
                x=fa[x][i];
        }    
    }
    if(x==y) return x;
    while(fa[x][0]!=fa[y][0])
    {
        for(int i=9;i>=0;--i){
            if(fa[x][i]!=0&&fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
        }        
    }
    return fa[x][0];
}
int main()
{
    int l,r;
    for(int i=1;i<=15;++i){
        cin>>l>>r;
        E[l].push_back(r); 
    }
    dfs(0,1);
    int a,b,t=100;
    while(t--)
    {
        cin>>a>>b;
        cout<<lca(a,b)<<endl;
    }
    
    
    return 0;
}
/*
输入图: 
1 2
2 4
4 5
5 6
5 7
7 10
10 11
10 12
11 13
12 15
13 14
1 3
3 8
8 9
9 16
*/
View Code

转载于:https://www.cnblogs.com/Remilia-Scarlet/p/10763636.html

内容概要:本文详细探讨了基于MATLAB/SIMULINK的多载波无线通信系统仿真及性能分析,重点研究了以OFDM为代表的多载波技术。文章首先介绍了OFDM的基本原理和系统组成,随后通过仿真平台分析了不同调制方式的抗干扰性能、信道估计算法对系统性能的影响以及同步技术的实现与分析。文中提供了详细的MATLAB代码实现,涵盖OFDM系统的基本仿真、信道估计算法比较、同步算法实现和不同调制方式的性能比较。此外,还讨论了信道特征、OFDM关键技术、信道估计、同步技术和系统级仿真架构,并提出了未来的改进方向,如深度学习增强、混合波形设计和硬件加速方案。; 适合人群:具备无线通信基础知识,尤其是对OFDM技术有定了解的研究人员和技术人员;从事无线通信系统设计与开发的工程师;高校通信工程专业的高年级本科生和研究生。; 使用场景及目标:①理解OFDM系统的工作原理及其在多径信道环境下的性能表现;②掌握MATLAB/SIMULINK在无线通信系统仿真中的应用;③评估不同调制方式、信道估计算法和同步算法的优劣;④为实际OFDM系统的设计和优化提供理论依据和技术支持。; 其他说明:本文不仅提供了详细的理论分析,还附带了大量的MATLAB代码示例,便于读者动手实践。建议读者在学习过程中结合代码进行调试和实验,以加深对OFDM技术的理解。此外,文中还涉及了些最新的研究方向和技术趋势,如AI增强和毫米波通信,为读者提供了更广阔的视野。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值