算法奇妙屋(四十七)-ST表

文章目录

⭐️ 博主在学习ST表时, 看的是b站的 STUACM-算法讲堂-ST表(区间最值问题)

一. 洛谷 P3865 【模板】ST 表 & RMQ 问题

翻译为人话 -> 求数组中, 任意区间的最大值

1. 常规动态规划解法

当数据量较少时, 区间最值查询可以使用常规的动态规划, 把所有区间的最大值都枚举了出来, 存放到了dp表总, 时间与空间复杂度为O(N^2), 查询的时间复杂度为O(1)

(1) 算法原理

(2) 代码

java 复制代码
    // 动态规划 i <= j, 区间[i,j]
    public static void dpFun(int[] arr) throws IOException {
        int n = arr.length;
        int[][] dp = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                if (i == j) dp[i][j] = arr[j];
                else dp[i][j] = Math.max(dp[i][j - 1], arr[j]);
            }
        }
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line;
        while ((line = br.readLine()) != null) {
            StringTokenizer st = new StringTokenizer(line);
            System.out.println(dp[Integer.parseInt(st.nextToken())][Integer.parseInt(st.nextToken())]);
        }
    }

2. ST表

对于这种静态查询, 且重叠区间最终得到的结果一致, ST表一般是最佳解法, ST表是通过倍增思想, 对于dp表的一种时间和空间的优化, 只能解决特定场景, 时间与空间复杂度为O(N * logN), 查询的时间复杂度为O(1)

(1) 算法原理

从使用到创建ST表, 每个变量的含义也都在旁边标注

(2) 代码

因为在创建和使用ST表过程中会频繁使用log2向下取整这个函数, 因此要封装起来

java 复制代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class ST {
	public static void main(String[] args) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer s = new StringTokenizer(br.readLine());
		int n = Integer.parseInt(s.nextToken());
		int m = Integer.parseInt(s.nextToken());
		 // 最大的无非就是从0开始延伸的,最接近末尾的,2的次幂的长度的区间, 可能包含log2(n)
		int[][] st = new int[n][log2(n) + 1]; // 对应dp表 [i][i + 2^j -1] 左右端点
		s = new StringTokenizer(br.readLine());
		for (int i = 0; i < n; i++) {
			st[i][0] = Integer.parseInt(s.nextToken());//初始化dp表
		}
		// 填写dp表
		//万万要注意,这里j表示区间的2^j长度,不是右端点
		for(int j = 1; j <= log2(n); j++) { 
			// 这里i是左端点, (i + (1 << j) - 1) 表示的是区间的右端点, 使其<n, 意义是在i从0开始遍历时,确保加上2^j个长度后,右端点不越界,越界了就没有意义了
			for (int i = 0; (i + (1 << j) - 1) < n; i++) {
				st[i][j] = Math.max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
			}
		}
		// 查询
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < m; i++) {
			s = new StringTokenizer(br.readLine());
			int l = Integer.parseInt(s.nextToken()) - 1; // 查询的区间下标==输入的数字-1
			int r = Integer.parseInt(s.nextToken()) - 1;
            int j = log2(r - l + 1);
            sb.append(Math.max(st[l][j], st[r - (1 << j) + 1][j])).append('\n');
		}
		System.out.println(sb);
	}
	public static int log2(int num) {
		return 31 - Integer.numberOfLeadingZeros(num);
	}
}

二. 洛谷 P2880 [USACO07JAN] Balanced Lineup G

1. 题目解析

这道题翻译成人话, 就是求任意区间中, 最大值与最小值的差

2. 算法原理

这道题依旧是使用ST表, 可以建立两个st表, stMax用来储存任意2^n长度的区间的最大值, stMin用来储存任意2^n长度的区间的最小值
具体如何使用和建立ST表, 在上面的模版

3. 代码

java 复制代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	// 经典的ST应用问题, 也可以使用线段树或者树状数组
	public static void main(String[] args) throws Exception {
		int n, q;
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer s = new StringTokenizer(br.readLine());
		n = Integer.parseInt(s.nextToken());
		q = Integer.parseInt(s.nextToken());
		int[][] stMin = new int[n][log2(n) + 1]; // st[i][j] -> dp[i][i + 2^j - 1]
		int[][] stMax = new int[n][log2(n) + 1];
		// 初始化st数组
		for (int i = 0; i < n; i++) stMin[i][0] = stMax[i][0] = Integer.parseInt(new StringTokenizer(br.readLine()).nextToken());
		// 填表
		for (int j = 1; j <= log2(n); j++) {
			// 以i为起点时, 右端点不能超过n
			for (int i = 0; (i + (1 << j) - 1) < n; i++) {
				stMin[i][j] = Math.min(stMin[i][j - 1], stMin[i + (1 << (j - 1))][j - 1]);
				stMax[i][j] = Math.max(stMax[i][j - 1], stMax[i + (1 << (j - 1))][j - 1]);
			}
		}
		// 查询
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < q; i++) {
			s = new StringTokenizer(br.readLine());
			int l = Integer.parseInt(s.nextToken()) - 1;
			int r = Integer.parseInt(s.nextToken()) - 1;
			sb.append(queryMax(stMax, l, r) - queryMin(stMin, l, r)).append('\n');
		}
		System.out.println(sb);
	}
	static int log2(int num) {
		return 31 - Integer.numberOfLeadingZeros(num);
	}
	static int queryMin(int[][] st, int l, int r) {
		int j = log2((r - l + 1));
		return Math.min(st[l][j], st[r - (1 << j) + 1][j]);
	}
	static int queryMax(int[][] st, int l, int r) {
		int j = log2((r - l + 1));
		return Math.max(st[l][j], st[r - (1 << j) + 1][j]);
	}
}
相关推荐
洛水水4 小时前
【力扣100题】18.随机链表的复制
算法·leetcode·链表
南宫萧幕4 小时前
规则基 EMS 仿真实战:SOC 区间划分与 Simulink 闭环建模全解
算法·matlab·控制
多加点辣也没关系5 小时前
数据结构与算法|第二十三章:高级数据结构
数据结构·算法
hoiii1877 小时前
孤立森林 (Isolation Forest) 快速异常检测系统
算法
c++之路8 小时前
适配器模式(Adapter Pattern)
java·算法·适配器模式
吴声子夜歌8 小时前
Java——接口的细节
java·开发语言·算法
myheartgo-on8 小时前
Java—方 法
java·开发语言·算法·青少年编程
宝贝儿好9 小时前
【LLM】第三章:项目实操案例:智能输入法项目
人工智能·python·深度学习·算法·机器人
雪碧聊技术10 小时前
上午题_算法
算法·软考·软件设计师