RMQ问题的线段树解法
来源:互联网 发布:java算法笔试题 编辑:程序博客网 时间:2024/05/19 18:37
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)-1int[] 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 RMQpublic 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 rootprivate 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 -1if (x>j || y<i) return -1; //if the query interval entirely covers the current interval//return the current node's minimum indexif(x<=i && y>=j){return M[node];}//split query intervalint leftnode=2*node+1;int rightnode=leftnode+1;int mid = (i+j)/2;if(x>mid){//right branchreturn query(M,rightnode,A,mid+1,j,x,y);}else if(y<=mid){//left branchreturn query(M,leftnode,A,i,mid,x,y);}else{//mixedint 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;elsereturn 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/indexSystem.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();}}}
- RMQ问题的线段树解法
- 线段树/RMQ问题
- RMQ问题的树状数组解法
- RMQ问题的三种解法
- LCA问题的RMQ解法解析
- RMQ问题 (st+线段树)
- HDU1754线段树解经典的RMQ问题
- poj3264(RMQ问题的线段树实现方法)
- #bzoj3186#单点修改的RMQ问题(zkw线段树版)
- #bzoj3187#区间修改的RMQ问题(zkw线段树版)
- 基于线段树的RMQ
- HiHo #1070 && 1077 : RMQ问题再临 【RMQ-线段树】
- hunnu11460(线段树解RMQ问题)
- #1077 : RMQ问题再临-线段树
- 线段树+RMQ区间最值问题
- 1077 RMQ问题再临-线段树
- poj 3264 RMQ问题 zkw线段树
- RMQ问题再临 (线段树)
- 逆向分析 C++继承内存分布(带虚函数) 及动态绑定实现
- 论ACM与泡妞
- hdu1114!完全背包!(一维数组)
- NSURLConnection同步,异步与SSL
- Linux下密码过期时间设置 (chage 的设置)
- RMQ问题的线段树解法
- 启点在何处?
- UITextField+总结
- 让VC程序拥有XP风格样式
- 给linux发行版增加一个内核选项
- 探测python中字符集模块chardet
- JS throw语句
- ORACLE VARCHAR 排序问题
- 常用JS代码收集常用JS代码收集