zoukankan      html  css  js  c++  java
  • RMQ模板

    RMQ 即范围最小值问题 (Range Minimum Query)。给出一个 n 个元素的数组,设计一个数据结构,支持查询操作 Query(L, R):计算该区间内的最小值。

    如果用朴素的算法的话,每一次一个循环求解,那时间复杂度就达到了 O(mn),显然不够快。在实践中,最常用的是 Tarjan 的 Sparse - Table(st 表)算法,主要功能是解决静态区间最值问题。它用 O(nlogn)预处理,而查询只用 O(1),且常数很小。

    这个算法具体是这么写的:开一个二维数组 dp[i][j] ,表示从 i 开始的,长度为 2 ^ j 的一段元素的最小值,这样就可以用递推的方法计算 dp[i][j] : dp[i][j] = min(dp[i][j - 1], dp[i + 2 ^ (j - 1)][j - 1])。原理如下图所示:

    可以看出,这是利用归并的思想。

    因为2^j <= n,因此dp数组的元素个数不超过 nlogn 个,而且每一项都可以在常数时间计算完毕,所以时间复杂度为 O(nlogn)。上代码

     1 void RMQ_init()
     2 {
     3     for(int i = 1; i <= n; ++i) dp[i][0] = a[i];    //此时长度为1,个只有一个元素。 
     4     for(int j = 1; (1 << j) <= n; ++j)    
     5     /*长度一定要放在外层循环,这样才能保证每一个元素都被更新到 ,
     6     跟区间dp原理一样*/
     7         for(int i = 1; i + (1 << j) - 1 <= n; ++i)
     8         {
     9             dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
    10         }
    11 }

    查询也很简单,令 k 满足 2 ^ k <= R - L + 1 的最大整数,则以 L 开头,R 结尾的两个长度为2 ^ k 的区间合起来就覆盖了要查询的区间 [L, R]。由于是取最小值,因此两个子区间有重叠的部分也没关系,代码如下

    1 int query(int L, int R)
    2 {
    3     int k = 0;
    4     while(1 << (k + 1) <= R - L + 1) k++;    
    5     //注意是 k + 1,而不是k,因为这么写的话,符合条件k还要再+1,就应返回dp[L][k - 1]了 
    6     return min(dp[L][k], dp[R - (1 << k) + 1][k]);
    7 }

    不过这样查询的时间复杂度就是 O(logn)。只要初始化 k,开一个数组 que[i],代表长度为 i 时 k 的取值,查询的复杂度就能达到 O(1)了。

    int k = 0;
    for(int i = 1; i <= n; ++i) {
        if ((1 << k) <= i) k++; 
        que[i] = k - 1;
    }
    
    void query(int L, int R)
    {
        int k = que[R - L + 1];
        return min(dp[L][k], dp[R - (1 << k) + 1][k]);
    }
  • 相关阅读:
    单链表反转的2种常见方法
    LeetCode解题报告:Reorder List
    LeetCode解题报告:Binary Tree Postorder Traversal
    LeetCode解题报告:LRU Cache
    LeetCode解题报告:Insertion Sort List
    Java编程杂记
    如何对一个不断更新的HashMap进行排序
    Python快速入门
    Html与CSS快速入门01-基础概念
    JVM快速入门
  • 原文地址:https://www.cnblogs.com/mrclr/p/8450119.html
Copyright © 2011-2022 走看看