leetcode1574. 删除最短的子数组使剩余数组有序(Python3、c++)

文章目录

  • leetcode1574. 删除最短的子数组使剩余数组有序
    • 方法:双指针
      • 思路:
      • 代码:
        • Python3:
        • cpp:
      • 结果:

leetcode1574. 删除最短的子数组使剩余数组有序

给你一个整数数组 arr ,请你删除一个子数组(可以为空),使得 arr 中剩下的元素是 非递减 的。

一个子数组指的是原数组中连续的一个子序列。

请你返回满足题目要求的最短子数组的长度。

示例 1:

输入:arr = [1,2,3,10,4,2,3,5]
输出:3
解释:我们需要删除的最短子数组是 [10,4,2] ,长度为 3 。剩余元素形成非递减数组 [1,2,3,3,5] 。
另一个正确的解为删除子数组 [3,10,4] 。

示例 2:

输入:arr = [5,4,3,2,1]
输出:4
解释:由于数组是严格递减的,我们只能保留一个元素。所以我们需要删除长度为 4 的子数组,要么删除 [5,4,3,2],要么删除 [4,3,2,1]。

示例 3:

输入:arr = [1,2,3]
输出:0
解释:数组已经是非递减的了,我们不需要删除任何元素。

示例 4:

输入:arr = [1]
输出:0

提示:

  • 1 <= arr.length <= 10^5
  • 0 <= arr[i] <= 10^9

方法:双指针

思路:

本题可以使用栈的方法解决。

本题删除的子数组只有三种情况,即从头开始的左边某子数组、中间的某子数组、从某点到最后的子数组。而这三种情况本质上可以当做是一种情况,即都当做是从中间删除的(从头删除可以当做左边数组为空,删除右边的数组可以当做右边数组为空)。

因此我们需要找到左边的非递减数组和右边的非递减数组,此时中间的部分是无序的,因此需要删除。因为我们不能保证左边数组的最大值小于等于右边数组的最小值,因此,我们还需要调整两个边界(即需要删除的子数组的左右边界)。

我们使用指针right来保存左边的非递减数组右边界下标,当遇到递减情况则停止。然后继续遍历,找到右边的非递减数组的左边界left。

那么我们如何判断删除数组的左右边界呢?

一个简单的方法是遍历左边数组的所有可能下标和右边数组的所有可能下标,两两配对当做删除数组的左右下标,看此时剩余数组是否满足为非递减数组,找到删除长度最小的情况。但是这样时间复杂度较高。

因为左边数组和右边数组都是有序的,因此我们可以这样进行判断:

  • 指针i表示最后右边数组接入左边数组的起点,即i-1为删除数组的右边界。
  • 我们的i从n-1开始向前遍历到left,位于i点时,如果此时arr[i]>=arr[right],那么左边的数组和右边的数组正好可以凑成非递减数组,删除数组的左边界为right-1,那么删除的数组长度为i-right-1
  • 如果此时arr[i],那么此时,i点继续缩小的话同样有这个关系式,构不成非递减数组,那么此时就应该缩小左边数组的长度了,即调整删除数组的左边界,right–,直到arr[i]>=arr[right],再继续计算。
  • 维护一个变量,保存最小的删除长度。
  • 这种双指针的做法大大降低了时间复杂度,时间复杂度为O(n)。

代码:

Python3:

class Solution:
    def findLengthOfShortestSubarray(self, arr: List[int]) -> int:
        # right表示左边数组的右边界
        right = 0
        n = len(arr)
        res = 0
        i = 1
        # 从头开始的递增序列
        while i < n and arr[i] >= arr[right]:
            right = i
            i += 1
        # res初始化为后面所有的长度(即只留下左边的有序数组,后边全删除,这是最大的res值)
        res = n-1-right
        left = i
        # 找到尾部的递增有序序列的起点left
        for i in range(left+1,n):
            if arr[i] >= arr[i-1]:
                pass
            else:
                left = i
        # print(left)
        #[left,n-1]是非递减有序的
        # 可以从右边的有序数组中找某个点i开始接入左边的有序数组中形成新的完整的有序数组
        # 这样只需要删除中间的子数组即可,找到删除最小的情况
        for i in range(n-1,left-1,-1):
            while right >= 0 and arr[i] < arr[right]:
                right -= 1
            res = min(res,i-right-1)
        return res      

cpp:

class Solution {
     
public:
    int findLengthOfShortestSubarray(vector<int>& arr) {
     
        // right表示左边数组的右边界
        int right = 0, n = arr.size(),res = 0,i = 1;
        // 从头开始的递增序列
        while (i < n && arr[i] >= arr[right]){
     
            right = i;
            i ++;
        }  
        // res初始化为后面所有的长度(即只留下左边的有序数组,后边全删除,这是最大的res值)
        res = n-1-right;
        int left = i;
        // 找到尾部的递增有序序列的起点left
        for (int i = left+1;i < n; i++){
     
            if (arr[i] >= arr[i-1])
                continue;
            else
                left = i;
        }    
        // [left,n-1]是非递减有序的
        // 可以从右边的有序数组中找某个点i开始接入左边的有序数组中形成新的完整的有序数组
        // 这样只需要删除中间的子数组即可,找到删除最小的情况
        for (int i = n-1; i >= left ;i--){
     
            while (right >= 0 && arr[i] < arr[right])
                right --;
            res = min(res,i-right-1);
        }          
        return res;

    }
};

结果:

leetcode1574. 删除最短的子数组使剩余数组有序(Python3、c++)_第1张图片

leetcode1574. 删除最短的子数组使剩余数组有序(Python3、c++)_第2张图片

你可能感兴趣的:(Leetcode做题记录,算法,数据结构,leetcode,python,c++)