持续创作,加速成长!这是我参与「掘金日新计划 · 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]
方法
通过题目可以知道,希望通过找到一个分割点
以实现右边的数组都大于左边的数组。
其实最简单的方法就是定义一个分割的指针在数组中移动,之后分别判断左右边是否满足上述的限制条件。但是这种方法需要对数组进行两边遍历,如果数组的长度为 ,利用这种方法解决问题的时间复杂度是 , 因此如何在此基础上降低时间复杂度是关键。
上面的解决方法中需要对数组进行 次的遍历,我们可以换一个角度来思考问题,如果要保证分割后的右边的数组都大于左边的数组,这就需要满足在遍历过程中找到大于右边数组的最长序列。假设遍历数组方向是从左往右,就会出现一下两种情况:
- 当前遍历的数组值
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;
}
其中需要注意的是,在遍历过程中存在tempMax
和max
。max
的作用是记录当前满足分割情况时的左边数组最大值,tempMax
是临时记录的左边数组真正最大的值(在不满足分割条件时进行更新)
- 当出现小于
max
值的情况时,则将max
值更新至最最新值tempMax
。 - 因为只有当
tempMax
大于max
值时才会对最大值进行更新。 - 在遍历判断时判断的是小于
max
的值。如果数组num[i]
小于max
,那么它也一定小于tempMax
。
这么做的主要原因是为了获得最长满足条件的右边数组。