leetcode 915. 分割数组

79 阅读1分钟

1. 题目与解析

给定一个数组 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]

根据题意,如果想在满足题意的情况下将数组分为两部分,只需要保证left中的最大值比right中的最小值还要小就可以了,因此,我们只需要找到每一位置i对应的leftMaxrightMin进行比较就可以得到答案。从开始到位置i的最值可以借助dp动态规划的思想进行求解,我们只需要比较nums[i]leftMax[i-1],并且取其中更大的一个赋予leftMax[i]即可得到答案。

leftMax[0] = nums[0];
for (int i = 1; i < len; i++) {
    leftMax[i] = Math.max(leftMax[i-1], nums[i]);
}

rightMax求解的思路与上面是一致的只是顺序是从后向前反过来而已。

rightMin[len-2] = nums[len-1];
for (int i = len - 3; i >= 0; i--) {
    rightMin[i] = Math.min(rightMin[i+1], nums[i+1]);
}

这是简单的涉及到范围的动态规划方法的使用,可以在平时练习的过程中加一总结与归纳。

2. 题解

class Solution {
    public int partitionDisjoint(int[] nums) {
        int len = nums.length;
        int[] leftMax = new int[len], rightMin = new int[len];
        leftMax[0] = nums[0];
        for (int i = 1; i < len; i++) {
            leftMax[i] = Math.max(leftMax[i-1], nums[i]);
        }
        rightMin[len-2] = nums[len-1];
        for (int i = len - 3; i >= 0; i--) {
            rightMin[i] = Math.min(rightMin[i+1], nums[i+1]);
        }

        for (int i = 0; i < len; i++) {
            if (leftMax[i] <= rightMin[i]) {
                return i + 1;
            }
        }

        return -1;
    }
}

另外,我们可以选择性的维护leftMaxrightMin中的一个,另一个的维护过程与求解过程结合到一起,从而减小时间复杂度与空间复杂度。