数据结构之稀疏表

稀疏表

适用场景

稀疏表解决的是区间最值问题(RMQ),对于多次查找区间最值的场景,稀疏表的作用是能够优化时间复杂度,常规的单次查找区间最值的时间复杂度为O(n),使用稀疏表能够将单次区间最值的查找时间均摊到O(logn)。当然对于区间最值查找的优化还存在诸如线段树等数据结构。

基本思路

稀疏表的构成主要分两个部分,一、建表,二、查找。

建表

建表:采用动态规划的方式建表。以查找区间内的最小值为例,采用倍增法的思想。待查找的数组为nums,定义二维数组f[i][k]。含义:起始位置为i,区间长度为2^k的区间范围内的最小值。样例数组[2,5,3,78,2,0],f[2][1] = 0,解释为起始位置为2,长度为2^2 = 4的区间内([3,78,2,0])的最小值为0。动态规划问题的解决步骤,一、定义数组状态(f[i][k]已定义),二、找到basecase,填充状态,三、根据递推关系式,填充数组。
这里的basecase很显然,f[i][0] = nums[i],区间[i,i]的最小值就是nums[i]。
递推关系式为:f[i][k] = min(f[i][k - 1],f[i + 2^(k - 1)][k - 1])。解释为将区间[i,i + 2^k]分成两个子区间[i,i + 2^(k - 1) - 1],[i + 2^(k - 1),i + 2^k],两个区间的最小值取最小就是大区间的最小值。

建表复杂度分析

建表的时间复杂度:O(nlogn)、空间复杂度:O(nlogn)

查找

查找函数:对于任意长的区间[left,right],首先确定该区间能分为多大的两个子区间(确定k的过程),然后将区间分成f[left][k - 1],f[j][k - 1];这里的j应该通过计算得出。这里值得注意的是分成的两个区间可能会存在重叠,区间一是当前[left,right]以left为起点,长度为2^(k - 1)的区间,区间二是当前[left,right]以right为终点,长度为2^(k - 1)的区间。区间重叠并不会影响最值的选取。

查找复杂度分析

查找的时间复杂度:O(1)、空间复杂度:O(1)

代码
#include <bits/stdc++.h>
using namespace std;

// 解决区间最值问题(RMQ)
// 初始化时间复杂度为 O(nlogn),空间复杂度 O(nlogn)
// 查找函数时间复杂度 O(1)
class ST{
	vector<int> nums;
	vector<vector<int> > f;
	void init(vector<int>& nums){
		int n = nums.size();
		int d = (int)log2(n);
		for (int i = 0;i < n;++i) f[i][0] = i;
		for (int j = 1;j <= d;++j){
			for (int i = 0;i < n;++i){
				f[i][j] = f[i][j - 1];
				int r = i + (1 << (j - 1));
				if (r < n && nums[f[r][j - 1]] < nums[f[i][j - 1]]) {
					f[i][j] = f[r][j - 1];
				}
			}
		}
	}
public:
	ST(vector<int>& nums){
		this->nums = nums;
		int n = nums.size();
		int d = (int)log2(n);
		f = vector<vector<int> >(n,vector<int>(d + 1)); 
		init(nums);
	}
	// 查找当前区间的最小值,返回其索引
	// 当存在多个最小值返回索引最小的
	int query(int left,int right){
		int len = right - left + 1;
		int d = (int)log2(len);
		int L = f[left][d],R = f[right - (1 << d) + 1][d];
		if (nums[L] <= nums[R]) return L;
		return R;
	}
};

int main(void){
	vector<int> A{9,1,0,67,2,43,12,55,1,0,8};
	ST st(A);
	int n = A.size();
	for (int i = 0;i < n;++i){
		for (int j = i;j < n;++j){
			cout << A[st.query(i,j)] << "  ";
		}
		cout << endl;
	}
	return 0;
}
样例代码结果分析

样例结果

关键词

关键词:区间最值问题(RMQ)、动态规划、倍增法

注意事项

值得注意的问题,查找区间,将区间分为两个子区间时可能会出现重叠,但是最值问题不会受重叠的影响。如果将该方法运用到其他场景的其他问题中,必须保证区间重叠时,结果不受影响。

小结

稀疏表这种数据结构的作用体现在对于区间最值查找的时间优化上,有很好的通用性,值得了解。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值