【今日算法】Leetcode 581.最短无序连续子数组 和 42.接雨水

最短无序连续子数组

题目

  • 给你一个整数数组 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;
    }
};
相关推荐
Σίσυφος19001 小时前
线性与非线性 、齐次非齐次
算法
(❁´◡`❁)Jimmy(❁´◡`❁)2 小时前
4815. 【NOIP2016提高A组五校联考4】ksum
算法
wm10432 小时前
代码随想录第四天
数据结构·链表
无限码力2 小时前
科大讯飞秋招笔试真题 - 字符拼接 & 字典序最小的字符串拼接 & 圆心覆盖
算法·秋招·科大讯飞·科大讯飞笔试真题
Lips6112 小时前
第四章 决策树
算法·决策树·机器学习
CoderCodingNo2 小时前
【GESP】C++六级考试大纲知识点梳理, (3) 哈夫曼编码与格雷码
开发语言·数据结构·c++
牛马大师兄2 小时前
数据结构复习 | 什么是数据结构?
数据结构
YuTaoShao2 小时前
【LeetCode 每日一题】2053. 数组中第 K 个独一无二的字符串
算法·leetcode·职场和发展
朔北之忘 Clancy3 小时前
第二章 分支结构程序设计(3)
c++·算法·青少年编程·竞赛·教材·考级·讲义