算法奇妙屋(四十七)-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]);
	}
}
相关推荐
ZhengEnCi3 小时前
Agent 架构怎么选:从单 Agent 到多 Agent 编排的选型决策指南
算法·架构
秋天的怀念_David3 小时前
第二十二夜 · 数字迷雾
java·程序人生·算法
Pniubi3 小时前
力扣283移动零(双指针法)
算法·leetcode·职场和发展
hhb_6184 小时前
大模型落地三重优化实战
人工智能·算法·机器学习
云和数据.ChenGuang4 小时前
光哥说Python RAG 系统需要哪些评估指标:从检索到生成的全链路评测指南
网络·人工智能·python·算法·langchain
2601_970703835 小时前
工业除湿机租赁:把一份租金合同拆开看
大数据·数据结构·算法·数据库架构·推荐算法
心中有你02145 小时前
Java实现Dijkstra最短路径算法(邻接表 + 优先队列)
java·数据库·算法
ZhengEnCi6 小时前
同样的模型,为什么本地和云 API 差别那么大:量化精度、聊天模板与采样参数的三大元凶
人工智能·算法·llm
Flynn Liang7 小时前
RAG 答非所问/幻觉怎么破?Spring AI QuestionAnswerAdvisor 召回优化实战(TopK/重排/混合检索)
人工智能·算法·spring·rag·幻觉·springai
liminla!7 小时前
SQL 从入门到进阶:语法速查与实战练习(完整优化版)
数据库·sql·算法