RMQ问题的ST算法

来源:http://www.cnblogs.com/ljsspace/archive/2011/08/04/2127317.html

他博客里好多在线算法,本篇是计算O(1)时间查询无序数组某段的最小值。

ST(Sparse Table)算法的基本思想是,预先计算从起点A[i]开始长度为2的j次方(j=0,1...logn)的区间的最小值,然后在查询时将任何一个区间A[i..j]划分为两个预处理好的可能重叠的区间,取这两个重叠区间的最小值。

在预处理阶段,从起点A[i]开始,任何一个长度为2^j的区间都可以划分为两个长度2^(j-1)的区间,其中第一个区间的范围为:i...i+2^(j-1)-1;第二个区间的范围为:i+2^(j-1)...i+2^j-1。用M[i,j]表示从A[i]开始,长度为2^j的区间(即A[i]...A[i+2^j-1])最小值对应的下标,那么A[M[i,j]] = min{A[i...i+2^(j-1)-1], A[i+2^(j-1)...i+2^j-1]}。  利用DP思想,先计算M[i,j-1]的值,然后计算M[i,j]的值。

在查询阶段,任何区间A[i..j]的长度d=j-i+1,令k=floor(logd),那么该区间可以被两个长度为2^k的子区间完全覆盖,这两个长度为2^k的区间可以有重叠。由于这两个区间已经在预处理中求得最小值,因此可以取二者的最小值得到A[i..j]的最小值。

ST算法预处理阶段的复杂度为O(nlogn),查询阶段的复杂度为O(1)。

实现:

?
/**
  *
  * Using ST(Sparse Table) algorithm to solve RMQ problem
  * time complexity: <O(nlogn),O(1)>
  *
 
  * Copyright (c) 2011 ljs (http://blog.csdn.net/ljsspace/)
  *
  * @author ljs
  * 2011-08-02
  *
  */
public class RMQ_ST {
     
     //ST: O(nlogn) for preprocessing
     public int [][] preprocess( int [] A){
         int n = A.length;
         //floor value
         int maxJ=( int )(Math.log(n)/Math.log( 2 ));
         
         int [][] M = new int [n][maxJ+ 1 ];
         
         //initial condition for dynamic programming: the RMQ for interval length=1
         for ( int i = 0 ; i < n; i++)
               M[i][ 0 ] = i;
         
         //dynamic programming: compute values from smaller(j=1) to bigger intervals
         for ( int j = 1 ; j<=maxJ; j++){
             for ( int i = 0 ; i + ( 1 << j) - 1 < n; i++){
                 int nexti = i + ( 1 << (j - 1 ));
                 if (A[M[i][j - 1 ]] <= A[M[nexti][j - 1 ]])
                     M[i][j] = M[i][j - 1 ];
                 else
                     M[i][j] = M[nexti][j - 1 ];
             }
         }
         return M;
     }
     
     //ST: O(1) for querying
     public int query( int [] A, int [][] M, int i, int j){
         if (j<i){
             //swap i and j
             int tmp=i;i=j;j=tmp;
         }
         int k = ( int )(Math.log(j-i+ 1 )/Math.log( 2 ));
         //the first interval
         int mina = M[i][k];
         int minb = M[j-( 1 <<k)+ 1 ][k];
         if (A[mina]<=A[minb])
             return mina;
         else
             return minb;
     }
 
     public static void main(String[] args) {       
         int [] A= new int []{ 0 , 1 , 2 , 3 , 7 , 1 , 9 , 2 , 8 , 6 };
         RMQ_ST st = new RMQ_ST();
         int [][] M = st.preprocess(A);
         
         
         System.out.format( "%n***********************%n" );      
         int i= 3 ,j= 7 ;       
         int min = st.query(A,M, i,j);
         System.out.format( "RMQ for A[%d..%d]: A[%d]=%d" , i,j,min,A[min]);
         
         System.out.format( "%n***********************%n" );
         j= 3 ;i= 7 ;   
         min = st.query(A,M, i,j);
         System.out.format( "RMQ for A[%d..%d]: A[%d]=%d" , i,j,min,A[min]);
         
         System.out.format( "%n***********************%n" );
         for ( int x= 0 ;x<A.length;x++){
             for ( int y= 0 ;y<x;y++){
                 System.out.format( "    " );
             }
             for ( int y=x;y<A.length;y++){
                 int p = st.query(A,M,x,y);             
                 System.out.format( " %d/%d" ,A[p],p);
             }
             System.out.println();
         }  
     }
 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值