【今日算法】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;
    }
};
相关推荐
Gorway4 小时前
解析残差网络 (ResNet)
算法
拖拉斯旋风4 小时前
LeetCode 经典算法题解析:优先队列与广度优先搜索的巧妙应用
算法
Wect4 小时前
LeetCode 207. 课程表:两种解法(BFS+DFS)详细解析
前端·算法·typescript
灵感__idea18 小时前
Hello 算法:众里寻她千“百度”
前端·javascript·算法
Wect1 天前
LeetCode 130. 被围绕的区域:两种解法详解(BFS/DFS)
前端·算法·typescript
NAGNIP2 天前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
颜酱2 天前
单调栈:从模板到实战
javascript·后端·算法
CoovallyAIHub2 天前
仿生学突破:SILD模型如何让无人机在电力线迷宫中发现“隐形威胁”
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
从春晚机器人到零样本革命:YOLO26-Pose姿态估计实战指南
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
Le-DETR:省80%预训练数据,这个实时检测Transformer刷新SOTA|Georgia Tech & 北交大
深度学习·算法·计算机视觉