数据结构专题小结:RMQ问题

RMQ问题

范围最小值问题(Range Minimum Query)是指:给定一个n个元素的数组A[1],A[2]...A[n]。设计一个数据结构,支持查询操作Query(L,R):计算min{A[L],A[L+1]...A[R]}。

该问题在实践中常用Tarjan的Sparse-Table算法。它的预处理时间是O(N*logN),但查询只需要O(1),而且常数非常小。最重要的是,这个算法非常好写,而且不易写错。

(1)原理:该算法利用了分治法的思想,令d(i,j)表示从i开始的,长度为2^j的一段元素中的最小值。则不难发现如下公式成立:

d(i,j)=min{d(i,j-1),d(i+2^(j-1),j-1)}

注意到2^i≤n,因此d数组的元素个数不超过n*logn个,而每一项都能在常数时间内算完。因此总的时间复杂度是O(N*logN)。

#define N 1000
int d[N][N];
vector<int>A;
#define INF 100000000
void RMQ_init(const vector<int>&A)//初始化操作,所有元素放到vector中
{
	for (int i = 0; i < N;i++)
	for (int j = 0; j < N; j++)
		d[i][j] = INF;
	int n = A.size();
	for (int i = 0; i < n; i++)
		d[i][0] = A[i];
	for (int j = 1; (1 << j) <= n;j++)
	for (int i = 0; i + (1 << j) - 1 < n; i++)
		d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][i - 1]); 
}

查询操作也很简单,令k为满足2^k≤R-L+1的最大整数,则以L开头,以R结尾的两个长度为2^k的区间合起来即覆盖了查询区间[L,R]。由于是最小值,有些元素重复考虑了也没有关系。

 

int RMQ(int L, int R)
{
	int k = 0;
	while ((1 << (k + 1)) <= R - L + 1)k++;
	return min(d[L][k], d[R - (1 << k) + 1][k]);
}

更简洁的写法如下:

 

int RMQ(int L, int R)
{
	int k = log((double)(R - L + 1)) / log(2.0);
	return min(d[L][k], d[R - (1 << k) + 1][k]);
}



你可能感兴趣的:(rmq问题)