持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情
题目
leetcode 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 进行划分。
题解
划分后的两个子数组要满足题目要求,需要保证“数组前缀最大值”小于等于“数组后缀最小值”。
因此,我们可以先预处理出数组的后缀最小值,记录在 mi 数组中。
然后从前往后遍历数组,维护数组前缀的最大值 mx,当遍历到某个位置时,如果数组前缀最大值小于等于数组后缀最小值,那么当前位置就是划分的分界点,直接返回即可。
/**
* @param {number[]} nums
* @return {number}
*/
var partitionDisjoint = function(nums) {//离散化+排序
let arr = nums.map((val,index)=>({val,index}));
arr.sort((a,b)=>a.val-b.val);
let i = 0,max=-1;
for(;i<arr.length;i++){
max =Math.max(max,arr[i].index);
if(max === i) break;
}
return max+1;
}
var partitionDisjoint = function(nums) {//双指针
let left = 0,right = nums.length-1;
let leftMax = [nums[0]],rightMin = [];
rightMin[right] = nums[right];
while(left<right){
if(leftMax[left]<=rightMin[right]){
right--;
rightMin[right] = Math.min(rightMin[right+1],nums[right]);
}else{
left++;
leftMax[left] = Math.max(leftMax[left-1],nums[left]);
}
}
//console.log(left,right,leftMax,rightMin);
while(leftMax[left]>rightMin[++right]){
left++;
leftMax[left] = Math.max(leftMax[left-1],nums[left]);
}
return left+1;
};
代码详解
minRight[i]储存的是nums数组当前下标nums[i]到数组尾nums[numsSize-1]这一段的最小值。因为范围是i到numsSize-1,所以从数组尾开始倒序循环。这是第一次遍历,目的是更新minRight数组的值。
第二次遍历则是不断更新maxLeft,并与minRight[i]比较。maxLeft存储了nums[0]到当前下标的nums[i]的最大值。 在第二次循环中,i作为left与right两个数组的分界点,nums[0]到nums[i]为left,nums[i+1]到nums[numsSize-1]则是right,maxLeft与minRIght[i+1]相比,即是left的最大值与right的最小值相比。