算法奇妙屋(四十七)-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]);
	}
}
相关推荐
kishu_iOS&AI2 小时前
Pytorch —— 自动微分模块
人工智能·pytorch·python·深度学习·算法·线性回归
北风toto2 小时前
深入解析JWT Token生成原理与安全加密技术详解
算法·安全·哈希算法
DeepModel3 小时前
通俗易懂讲透 EM 算法(期望最大化)
人工智能·python·算法·机器学习
Pentane.3 小时前
【力扣hot100】【Leetcode 15】三数之和|暴力枚举 双指针 算法笔记及打卡(14/100)
数据结构·笔记·算法·leetcode
不知名的老吴3 小时前
高阶函数的应用与函数对象概念
算法
Mr_pyx3 小时前
【LeetCode Hot 100】 - 缺失的第一个正数完全题解
数据结构·算法
wydxry3 小时前
深入解析自适应光学中的哈特曼波前传感技术:原理、算法与智能化前沿
大数据·人工智能·算法
xieliyu.3 小时前
Java顺序表实现扑克牌Fisher-Yates 洗牌算法
java·数据结构·算法·javase
ICscholar3 小时前
推荐系统常用指标NDCG含义及公式
人工智能·深度学习·算法