【每日一题】:915 分割数组

71 阅读3分钟

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

今天的每日一题是915. 分割数组 - 力扣(LeetCode),其实这道题目的暴力方法很容易得出答案,但是如何将时间复杂度降低才是重点。

题目

给定一个数组 nums ,将其划分为两个连续子数组 left 和 right, 使得:

  • left 中的每个元素都小于或等于 right 中的每个元素。
  • left 和 right 都是非空的。
  • left 的长度要尽可能小。

问:在完成这样的分组后返回 left 的 长度

实例

输入: nums = [5,0,3,8,6]
输出: 3
解释: left = [5,0,3],right = [8,6]

输入: nums = [1,1,1,0,6,12]
输出: 4
解释: left = [1,1,1,0],right = [6,12]

方法

通过题目可以知道,希望通过找到一个分割点以实现右边的数组都大于左边的数组。

其实最简单的方法就是定义一个分割的指针在数组中移动,之后分别判断左右边是否满足上述的限制条件。但是这种方法需要对数组进行两边遍历,如果数组的长度为 nn,利用这种方法解决问题的时间复杂度是 O(n2)O (n ^ 2), 因此如何在此基础上降低时间复杂度是关键。

上面的解决方法中需要对数组进行 nxnn x n 次的遍历,我们可以换一个角度来思考问题,如果要保证分割后的右边的数组都大于左边的数组,这就需要满足在遍历过程中找到大于右边数组的最长序列。假设遍历数组方向是从左往右,就会出现一下两种情况:

  • 当前遍历的数组值num[i]大于等于遍历过数组的最大值,则将长度加 1
  • 当前遍历的数组值num[i]小于遍历过数组的最大值,则将长度置0,重新进行计数

具体代码如下:

public int partitionDisjoint(int[] nums) {
        int max = nums[0];
        int tempMax = nums[0];
        // 右边数组的长度
        int count = 0;
        for (int i = 1; i < nums.length; i++){
            if (nums[i] >= max){
                // 如果当前数组值大于max,则计数加 1
                tempMax = Math.max(tempMax, nums[i]);
                count++;
            }else{
                // 如果当前数组值小于max,则计数置 0,同时更新max值
                max = tempMax;
                count = 0;
            }
        }
        // 左边长度 = 总长度 - 右边长度
        return nums.length - count;
    }

其中需要注意的是,在遍历过程中存在tempMaxmaxmax的作用是记录当前满足分割情况时的左边数组最大值,tempMax是临时记录的左边数组真正最大的值(在不满足分割条件时进行更新)

  • 当出现小于max值的情况时,则将max值更新至最最新值tempMax
  • 因为只有当tempMax 大于max值时才会对最大值进行更新。
  • 在遍历判断时判断的是小于max的值。如果数组num[i]小于max,那么它也一定小于tempMax

这么做的主要原因是为了获得最长满足条件的右边数组