持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情
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 <= 105
0 <= nums[i] <= 106
- 可以保证至少有一种方法能够按题目所描述的那样对
nums
进行划分。
解法一: 前后两次遍历
- 我们可以先从后遍历 目标数组,然后得出每个节点的当前最小值: right
- 然后我们可以从前遍历目标数组,根据题意
left
中的每个元素都小于或等于right
中的每个元素。- 如果
当前值
<=right
的时候 - 就是我们需要的结果 返回
i + 1
- 如果
var partitionDisjoint = function(nums) {
const n = nums.length;
const min = new Array(n).fill(nums[n - 1]);
// left 和 right 都是非空的 所以right必定有值
// 我们需要比对 i+1 所以从倒数第二项开始
for (let i = n - 2; i >= 0; i--) {
min [i] = Math.min(nums[i], min[i + 1]);
}
let max = 0;
for (let i = 0; i < n - 1; i++) {
max = Math.max(max, nums[i]);
if (max <= min[i + 1]) {
return i + 1;
}
}
return n - 1;
};
解法二:一次遍历
- 假设 我们已经有了分割点 left
- 那么我们可以得出 这个分割节点 中nums[0, left]的最大值:leftMax
- 又因为 left 中的每个元素都小于或等于 right 中的每个元素。
- 所以我们可以得到以下的结论:
- 如果 nums[i] < leftMax
- 那么就说明当前的节点不能在 right 中
- 这个时候 left 就要右移 left = i
- leftMax 就要进行重新维护为 nums[0, left]的最大值
- 最终的值 就是 left + 1
var partitionDisjoint = function(nums) {
let left = 0
let leftMax = nums [0]
let currMax = nums[0] // 每次移动期间的最大值
// nums.length - 1 是为了保证 left 和 right 都是非空的
for(let i = 0; i< nums.length - 1; i++) {
currMax = Math.max(currMax, nums[i])
if(nums[i] < leftMax){
left = i
leftMax = currMax
}
}
return left + 1
};