RMQ问题的树状数组解法

来源:互联网 发布:python 网络组织 编辑:程序博客网 时间:2024/05/16 02:57

树状数组中每个元素覆盖了不同长度的子区间,类似于稀疏表(ST)算法的思想,每一个数组元素存储了输入数列A在该区间的最小值下标。注意:这里树状数组不是用来存储区间累加值,而是区间的最小值下标。

这里针对预处理阶段提供两个算法:方法1(参考以下实现代码中的方法preprocess)采用类似于累加和中的update做法,每一个元素A[i]需要处理树状数组T中O(logn)个受影响的元素,因此预处理复杂度为O(nlogn)。方法2(参考以下实现代码中的方法preprocess2或preprocess3)利用DP(动态规划)思想,观察到树状数组中每一个下标为i的元素覆盖的区间都可以划分为:r个子区间 + 最后一个元素A[i]。(其中2^r=lowbit(i))因此只需要计算这r+1个区间中的最小值即可,该算法的复杂度为可以这样计算:对树中每一层所有元素基本操作做累加,例如叶子结点个数为n/(2^1),每一个叶子结点基本操作次数为1,倒数第二层的结点个数为n/(2^2),其中每一个结点的基本操作次数为2....所以总的基本操作次数为1 * n/(2^1) + 2 * n/(2^2) + 3 * n/(2^3) +  ...+ k * n/(2^k) + ... + logn * n/(2^logn) = O(n)。可见第二个方法比第一个方法复杂度要小。在以下的实现代码中,方法preprocess2或preprocess3是等效的,preprocess3使用了类似累加和中的query做法,即找出互不重叠的子区间。

查询时,采用递归做法,分两种情形: case 1) 如果查询的区间在当前区间中,则将当前区间最后一个元素剥离开来,使查询区间长度减1;case 2)如果查询的区间与当前区间没有重叠,则将当前区间所有元素剥离开来,使查询区间长度减去lowbit值(即2^r)。最糟情况下需要执行case 1)总共logn次,每次查询时树高度减1,所以查询的复杂度为(logn-1) + (logn-2)+...+1=O(logn*logn)。

跟ST算法或线段树算法相比,树状数组解RMQ问题的优势是存储空间小。预处理复杂度跟线段树算法相当(采用上面的方法2),但是查询的复杂度比线段树算法中的O(logn)要大。通过时间换空间达到时空平衡,再加上编程简单,树状数组算法还是有一定价值的。

注意:在下面的代码中数组A的下标永远都是从0开始,而树状数组T的有效下标从1开始(T[0]没有被利用)。另外,查询部分是tail recursion,实际上可以改写为迭代计算。

实现:

/** *  * RMQ algorithm using Binary Indexed Tree (BIT) *  * Copyright (c) 2011 ljs (http://blog.csdn.net/ljsspace/) * Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)  *  *  * @author ljs * 2011-08-10 * */public class RMQ_BIT {//each element is a min-val index in the range i-2^r +1 ...  iprivate int[] T; public RMQ_BIT(int n) {//each element in T is initialized to 0T = new int[n + 1];//init Tfor(int i=1;i<=n;i++){T[i] = -1;}}//preprocess method 1: create BIT - O(nlogn)public void preprocess(int[] A){int n = A.length;for(int i=0;i<n;i++){update(A,i);}}//A better preprocess method 2: create BIT - O(n)public void preprocess2(int[] A){int n = A.length;for(int i=1;i<=n;i++){int k = lowbit(i);T[i] = i-1; //initial value//each subrange's index is left shifted by 1 (=divide by 2)for(int j=1;j<k;j<<=1){//Note: use == for canonical RMQ resultif(A[T[i]]>=A[T[i-j]]){T[i] = T[i-j];}}}}//another form of preprocess2: similar to query in BIT public void preprocess3(int[] A){int n = A.length;for(int i=1;i<=n;i++){int j = i - lowbit(i);T[i] =  i - 1; //set the initial value of T[i]: i-1 is an index of Aint k = i - 1; //i-1 is the next index of Twhile(k>j){//Note: use == for canonical RMQ resultif(A[T[i]]>=A[T[k]]){T[i] = T[k];}k -= lowbit(k);}}} private int lowbit(int x){return x & (-x);}//idx is A's index, starting with 0private void update(int[] A,int idx) {int x = idx+1; //A's index starts with 0int val = A[idx];while (x < T.length) {if(T[x] ==-1 || A[T[x]] > val){T[x] = idx;}x += lowbit(x);}}//i,j are A's index, starting with 0public int query(int[] A,int i,int j){if(i>j){//swap  int tmp=i;i=j;j=tmp;}return query(A,i+1,j+1,j);}//precondition: p...q must be in the range: 1...n (inclusive)private int query(int[] A,int p,int q,int minIndex){if(p>q) return minIndex;int j = q - lowbit(q);if(p>j){//p..q is equal to or less than T[q]'s coverageif(A[q-1] <= A[minIndex])minIndex = q-1;return query(A,p,q-1,minIndex);}else{ // if(p<=j)//p..q is greater than T[q]'s coverageif(A[T[q]] <= A[minIndex])minIndex = T[q];return query(A,p,j,minIndex);}}private void reportLUTable(int[] A){System.out.format("%n***********************%n");for(int x=0;x<A.length;x++){System.out.format("%d..[%d-%d]",x,x,A.length-1);for(int y=x;y<A.length;y++){int p = query(A,x,y);System.out.format(" %d/%d",A[p],p);}System.out.println();}}public static void main(String[] args) {int[] A = new int[]{2,4,3,1,6,7,8,9,1,7};int n = A.length;RMQ_BIT bitRMQ = new RMQ_BIT(n);bitRMQ.preprocess3(A);int i=0;int j=3;int min = bitRMQ.query(A, i,j);System.out.format("RMQ for A[%d..%d]: A[%d]=%d", i,j,min,A[min]);bitRMQ.reportLUTable(A);////////////////////System.out.format("%n***********************%n");A=new int[]{10,15,34,20,7,5,18,68,29,40, //0..924,3,45,26,7,23,43,12,68,34,  //10..1926,34,33,12,80,57,24,42,77,27, //20..2956,33,23,32,54,13,79,65,19,33,  //30..3915,24,43,73,55,13,63,8,23,17};  //40..49n = A.length;bitRMQ = new RMQ_BIT(n);bitRMQ.preprocess3(A);i=0;j=10;min = bitRMQ.query(A,i,j);System.out.format("RMQ for A[%d..%d]: A[%d]=%d%n", i,j,min,A[min]);i=12;j=49;min = bitRMQ.query(A,i,j);System.out.format("RMQ for A[%d..%d]: A[%d]=%d%n", i,j,min,A[min]);i=20;j=46;min = bitRMQ.query(A,i,j);System.out.format("RMQ for A[%d..%d]: A[%d]=%d%n", i,j,min,A[min]);i=20;j=49;min = bitRMQ.query(A,i,j);System.out.format("RMQ for A[%d..%d]: A[%d]=%d%n", i,j,min,A[min]);}}


测试输出:


RMQ for A[0..3]: A[3]=1
***********************
0..[0-9] 2/0 2/0 2/0 1/3 1/3 1/3 1/3 1/3 1/3 1/3
1..[1-9] 4/1 3/2 1/3 1/3 1/3 1/3 1/3 1/3 1/3
2..[2-9] 3/2 1/3 1/3 1/3 1/3 1/3 1/3 1/3
3..[3-9] 1/3 1/3 1/3 1/3 1/3 1/3 1/3
4..[4-9] 6/4 6/4 6/4 6/4 1/8 1/8
5..[5-9] 7/5 7/5 7/5 1/8 1/8
6..[6-9] 8/6 8/6 1/8 1/8
7..[7-9] 9/7 1/8 1/8
8..[8-9] 1/8 1/8
9..[9-9] 7/9

***********************
RMQ for A[0..10]: A[5]=5
RMQ for A[12..49]: A[14]=7
RMQ for A[20..46]: A[23]=12
RMQ for A[20..49]: A[47]=8


原创粉丝点击