RMQ问题的线段树解法

RMQ(Range Minimum Query)问题是计算一个输入数列A[0...n-1]从位置i到位置j之间的最小值,即RMQ[i,j]=min{A[k], k=i,i+1...j}。RMQ的解法有很多,比如Sparse Table(ST)算法(注意这个ST缩写不是指Segement Tree哦)和转化为特殊的+1/-1 RMQ的算法。为了查询的方便,RMQ算法需要对数列A进行预处理(preprocessing),如果用<f(n),g(n)>分别表示RMQ算法的预处理复杂度和查询复杂度的话,用线段树(segment tree)解决RMQ问题的复杂度为<O(n),O(logn)>。

线段树解法首先构建线段树(一棵二叉树),根结点对应区间0...n-1,左右子树通过下标中间值分界并且没有重叠区间值,叶结点对应单值区间。如果设线段树根结点的高度为0,那么整棵树的高度不超过floor(logn)+1,按照完全二叉树的做法,将每个结点按level order存入一维数组M,数组M中每个元素值为该结点对应的区间[i...j]的最小值下标值k。虽然不能像完全二叉树那样保证元素之间没有空隙,但是可以利用下标值2k+1和2k+2快速访问某个结点k的左右孩子。查询时需要深度搜索树的结点,因此复杂度为O(logn),比起Sparse Table等算法的查询复杂度为O(1)要差一些。


实现:

/**
 * 
 * Using Segment Tree to solve RMQ problem
 * time complexity: <O(n),O(logn)>
 * 
 * RMQ(A)-Range Minimum Query on array A: find the minimum value/index in the range A[i..j]
 *  
 * 
 * 
 *  
 * Copyright (c) 2011 ljs (http://blog.csdn.net/ljsspace/)
 * Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php) 
 * 
 * @author ljs
 * 2011-08-02
 *
 */ 
public class RMQ_SegmentTree {	
	//return the heap-like array of the segment tree 
	//(Note: the segment tree is not a complete tree, but we
	//use an array to simulate a complete tree, so the array has some gaps)
	public int[] buildSegmentTree(int[] A){
		int n = A.length;
		int heapHeight = (int)(Math.log(n)/Math.log(2))+1;//h=floor(logN)+1; h starts with 0.
		int mSize = (1<<(heapHeight+1))-1; //total nodes = 2^(h+1)-1
		
		int[] M = new int[mSize];
		
		buildSegmentTree(M, 0, A, 0, n-1);
		return M;
	}
	
	private void buildSegmentTree(int[] M,int node,int[] A,int i,int j){		
		if(i==j){
			M[node] = i;
		}else{
			int leftnode=2*node+1;
			int rightnode=leftnode+1;
			
			buildSegmentTree(M,leftnode,A,i,(i+j)/2);
			buildSegmentTree(M,rightnode,A,(i+j)/2+1,j);
			
			if(A[M[leftnode]]<=A[M[rightnode]]){
				M[node] = M[leftnode];
			}else{
				M[node] = M[rightnode];
			}
		}
	}
	
	//x..y is the query interval of RMQ
	public int query(int[] M,int[] A,int x,int y){
		int n=A.length;
		return query(M,0,A,0,n-1,x,y);
	}
	
	//x..y is the query interval of RMQ
	//i..j is the current interval of a segment sub-tree's root
	private int query(int[] M,int node,int[] A,int i,int j,int x,int y){		
		//if the query interval doesn't intersect the current interval 
		//return -1
		if (x>j || y<i)
		    return -1;
		   
		//if the query interval entirely covers the current interval
		//return the current node's minimum index
		if(x<=i && y>=j){
			return M[node];
		}
		
		//split query interval
		int leftnode=2*node+1;
		int rightnode=leftnode+1;
		
		int mid = (i+j)/2;
		if(x>mid){
			//right branch
			return query(M,rightnode,A,mid+1,j,x,y);
		}else if(y<=mid){
			//left branch
			return query(M,leftnode,A,i,mid,x,y);
		}else{
			//mixed
			int p1 = query(M,leftnode,A,i,mid,x,y);
			int p2 = query(M,rightnode,A,mid+1,j,x,y);
			
			if(p1==-1)return p2;
			if(p2==-1)return p1;
			
			if(A[p1]<=A[p2])
				return p1;
			else
				return p2;
		}
	}

	public static void main(String[] args) {
		RMQ_SegmentTree rmqSegTree = new RMQ_SegmentTree();
	
		int[] A = new int[]{0,1,2,3,7,1,9,2,8,6};			
		int[] M = rmqSegTree.buildSegmentTree(A);
		
			
		//print RMQ table RMQ[i,j], each cell has the form: value/index
		System.out.println("RMQ table:");
		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 = rmqSegTree.query(M,A,x,y);				
				System.out.format(" %d/%d",A[p],p);
			}
			System.out.println();
		}		
	}

}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值