最短无序连续子数组
题目
- 给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。 请你找出符合题意的 最短 子数组,并输出它的长度。
- 示例1:
输入:nums = [2,6,4,8,10,9,15]
输出:5
解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
- 示例 2:
输入:nums = [1,2,3,4]
输出:0
- 示例 3:
输入:nums = [1]
输出:0
思路分析
根据题目要求,我们能够知道,所谓最短无序连续子数组就是通过将这个子数组进行升序排序,那么就可以让这个数组变成一个有序的数组,也就是说,如果我们将这个子数组从整个数组中剥离,那么这个数组就会变成一个有序的数组。
那么此时我们分析一下,什么样的数组才能叫作无序数组,我们以示例1当中的数组为例,它的最短无序子数组就是【6 4 8 10 9】,我们可以得到下面的结论:
- 这个无序数组里面必定会存在相邻的两个元素是降序排列,例如【6 4】和【10 9】,这两个降序元素中间的数组就是我们要找的最短无连续子数组
- 当我们去除了这个无序的子数组后,剩余的元素是有序的,以示例1为例,当我们除去了【6 4 8 10 9】这个子数组后,剩下的【2 9】是有序的
通过上面的分析我们发现,我们必须找到两个边界,分别是最短无序数组的左边界和右边界,因此我们必须定义两个指针,一个指针负责找到左边界,一个指针负责找到右边界
那么我们如何让两个指针找到左右边界呢?我们可以通过上面的分析发现,假设这个数组是有序的,那么如果我们从左往右更新指针的话,指针所经过的值是升序的;如果我们从右往左更新指针的话,指针所经过的值是降序的;
当我们从左往右更新的过程当中,如果发现当前指针的值是小于前一个值,那么我们就应该记录这个值所在的位置;当我们从右往左更新的过程中,如果发现当前指针的值大于后一个值,那么我们也应该记录这个值所在的位置
上面我们的指针在更新过程当中比较的是相邻的元素,那么如果说相邻的元素是相同的呢?我们上面的思路是不是就无法进行了,所以我们必须有一个变量来记录当前的参考值,我们发现当我们从左向右更新的过程当中,这个指针最终找到的是这个无序数组的右边界,而这个右边界的值通常是较大的值;当我们从右往左更新的过程中,这个指针最终找到的是这个无序数组的左边界,而这个左边界的值通常是较小的值,所以我们可以定义两个变量,分别指向较大值和较小值
代码书写
根据上面的分析,我们可以写下如下代码:
cpp
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
if(nums.size()<=1){return 0;}
int left=0,right=0;
int maxVal=nums[0],minVal=nums[nums.size()-1];
for(int i=1;i<nums.size();++i)
{
if(nums[i]>=maxVal){maxVal=nums[i];}
else{right=i;}
}
for(int i=nums.size()-1-1;i>=0;--i)
{
if(nums[i]<=minVal){minVal=nums[i];}
else{left=i;}
}
if(right==0){return 0;}
return right-left+1;
}
};
接雨水
题目
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
- 示例1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
- 示例2:
输入:height = [4,2,0,3,2,5]
输出:9
思路分析
首先我们来想一想,什么样的情况下才能接的到雨水?对于任意位置,如果要让该位置接到雨水,应该满足左边界的值>该位置的值且右边界的值>该位置的值;那么该位置能够接多少雨水取决于什么呢?是不是应该取决于min(左边界,右边界)
所以我们一定需要遍历这个数组,然后计算每一个位置能够接多少雨水,然后将这些雨水进行累加,这样我们就能知道整体能够接多少雨水了
因此我们一定需要定义两个指针,通过这三个指针去遍历这个数组,第一个指针指向左边界,第二个指针指向有边界,第三个指针指向当前的值,然后去判断每一个位置能够接多少雨水,然后对这些值进行累加。但这个思路有一个致命的问题,对于示例1,当我们走到[2,1,0,1,3]这里的时候,能够接四个单位的雨水,但是我们这个思路只能够接3个单位的雨水,因此我们并不能直接定义三个指针向后走。
那么我们应该怎么定义指针的移动方向呢?我们可以看到,之所以会出现这样的原因是因为我们把边界只控制了一个单位,即right-left=1,所以我们应该扩大我们要找的边界,所以我们应该从两边向中间去遍历,所以我们应该定义两个指针,分别指向左边界和右边界,然后去计算每一个位置能够接的雨水数量,那么对于任意位置能够接的雨水数量和上面的分析是一样的,取决于最小的边界,所以我们应该定义两个变量,分别去记录左边界和右边界的值,由此我们可以编写下面的代码
编写代码
cpp
class Solution {
public:
int trap(vector<int>& height) {
if(height.size()<3){return 0;}
int left=1,right=height.size()-1-1;
int leftSize=height[0],rightSize=height[height.size()-1];
int volume=0;
while(left<=right)
{
if(leftSize<rightSize)
{
if(height[left]<leftSize)
{
volume+=leftSize-height[left];
}
else
{
leftSize=height[left];
}
++left;
}
else
{
if(height[right]<rightSize)
{
volume+=rightSize-height[right];
}
else
{
rightSize=height[right];
}
--right;
}
}
return volume;
}
};