分割数组

86 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第26天,点击查看活动详情

第 26 天

分割数组

题目

给定一个数组 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 进行划分。

题解

拿到题首先想到是朴素的做法,就是每个点依次向左右扩展,如果发现左边的最大值一直小于右边的最小值,那么该点就是答案。但是该方法时间复杂度是O(n* n),看一下数据量应该不能过。

那就想到通过离散化排序,我们把数组的下标保存,然后排序,从左往右再遍历下如果排序的最大值和下标相同,说明从左往右能组成连续的且排序小于右半部分的数组。该方法的时间复杂度是O(n*logn);

双指针的办法也是一开始就想到的,优先缩减右半部分,但是发现会缩过头。后来也是加上数组保存前缀最大值和后缀最小值,然后往后去修正。看了题解才感觉有点low,哈哈哈。

/**
 * @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;
};