持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第26天,点击查看活动详情
915. 分割数组
给定一个数组 nums ,将其划分为两个连续子数组 left 和 right, 使得:
left中的每个元素都小于或等于right中的每个元素。left和right都是非空的。left的长度要尽可能小。
在完成这样的分组后返回 left 的 长度 。
用例可以保证存在这样的划分方法。
用例1
输入: nums = [5,0,3,8,6]
输出: 3
解释: left = [5,0,3],right = [8,6]
用例2
输入: nums = [1,1,1,0,6,12]
输出: 4
解释: left = [1,1,1,0],right = [6,12]
数据范围
2 <= nums.length <= 10^50 <= nums[i] <= 10^6
思路
题目要求的是分割为两个数组,前半部分的最大值小于等于后半部分的最大值: 就是:
要满足这个关系式意味着需要去计算两个区间的最值。
我们的划分点有 个划分点,意味着我们需要计算次最大值最小值,时间复杂度总体上是的
得想办法解决时间复杂度的问题:
这里给几种做法,思想都是基于优化区间查询时间的做法:
做法1:
线段树可以迅速求出一段区间的最大值和最小值,单次时间复杂度为,n次查询最大值最小值,总体时间复杂度为
做法2:
树状数组也可以迅速求出一段区间的最大值和最小值,并且比线段树好写不少,时间复杂度和线段树类似,总体
做法3:
ST表是一种高效的离线区间查询算法,和线段树、树状数组不同的是,ST表是基于倍增实现的一种区间查询最值查询算法。一般得要求区间具有重叠性 总体时间复杂度
做法4:
前面三种做法都是通用的做法,对于本题来说,可以更特殊一点,不用那么麻烦,因为第二个数组一定是从尾部到中间某个位置k的,这就导致了区间是只扩充不缩小的。
这个就给我们带来了很多便利。
因为有这样的性质:
所以,我们求一个后缀的min数组,就可以实现转移最大值了
同类前面所有的最大值也算一样的操作计算即可
use std::cmp::{max, min};
impl Solution {
pub fn partition_disjoint(nums: Vec<i32>) -> i32 {
let n = nums.len();
let mut mi = vec![0; n];
for i in (0..n).rev() {
if i == n - 1 {
mi[i] = nums[i]
} else {
mi[i] = min(mi[i + 1], nums[i])
}
}
let mut ma = nums[0];
for i in 0..n - 1 {
ma = max(ma, nums[i]);
if ma <= mi[i + 1] {
return (i+1) as i32;
}
}
-1
}
}