RMQ和LCA问题

RMQ问题

RMQ问题就是Range max/min Query

A数列为:3 2 4 5 6 8 1 2 9 7
对于数列A,回答查询RMQ(A,i,j)的最大/小值:返回在数组A中下标在i和j之间的最小/最大值。

最容易想到的问题是 O(n) 的查询,当查询数量过多的时候,时间花费可能很多。ST算法可以在 O(nlogn) 的时间内预处理完成后, O(1) 的时间内查询。

在线算法(ST算法)

在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。

A数列为:3 2 4 5 6 8 1 2 9 7

F[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。
同理

F[1,1] = max(3,2) = 3
F[1,2] = max(3,2,4,5) = 5
F[1,3] = max(3,2,4,5,6,8,1,2) = 8

并且我们可以容易的看出F[i,0]就等于A[i]。(DP的初始值)

F[i, j]=max(F[i,j-1], F[i + 2^(j-1),j-1])
比如F[1,3] = max(F(1,2),F(1+2^2,2))

void RMQ(int num) //预处理->O(nlogn),得到maxsum[i][j]以及minsum[i][j]的值
//i和j的范围是i + (1 << j) < num
{
    for(int j = 1; j < 20; ++j)
    for(int i = 1; i <= num; ++i)
    if(i + (1 << j) < num)
    {
        maxsum[i][j] = max(maxsum[i][j - 1], maxsum[i + (1 << (j - 1))][j - 1]);
        minsum[i][j] = min(minsum[i][j - 1], minsum[i + (1 << (j - 1))][j - 1]);
    }
}

在查询的时候
比如要求区间[2,8]的最大值F[2,8], k=log282+1)=2 ,即求:
F[2,8] = max(maxsum[2][2],maxsum[8-2^2+1][2]) = max(maxsum[2,2],maxsum[5,2]);

线段树

暂时空着。

LCA问题

 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点。

这里写图片描述

4和5的最近公共祖先是2
5和3的最近公共祖先是1
2和1的最近公共祖先是1

离线算法Tarjan

参考博客

下面详细介绍一下Tarjan算法的基本思路:

  1. 任选一个点为根节点,从根节点开始。
  2. 遍历该点u所有子节点v,并标记这些子节点v已被访问过。
  3. 若是v还有子节点,返回2,否则下一步。
  4. 合并v到u上。
  5. 寻找与当前点u有询问关系的点v。
  6. 若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。
Tarjan(u)// marge和find为 并查集 合并函数 和 查找函数
{
    for each(u,v)    //访问所有u子节点v
    {
        Tarjan(v);        //继续往下遍历
        marge(u,v);    //合并v到u上
        标记v被访问过;
    }
    for each(u,e)    //访问所有和u有询问关系的e
    {
        如果e被访问过;
        u,e的最近公共祖先为find(e);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值