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

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

力扣C++打卡(3.25)!✊✊✊🌈大家好!本篇文章将介绍关于数组的OJ题,题目来自力扣:1574. 删除最短的子数组使剩余数组有序,展示代码语言暂时为:C++代码 😇。


🔒1、题目:

给你一个整数数组 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
来源:力扣👈
链接:https://leetcode.cn/problems/shortest-subarray-to-be-removed-to-make-array-sorted/

☀️2、思路:

题目给一个整数数组 arr ,要求删除原数组中连续的一个子序列(可以为空),使得 arr 中剩下的元素是非递减的,需要求出满足这样条件的最短的子数组长度。

最容易想到的是先保证数组一端单调递增,对另一端元素进行处理。
从这个想法出发,选择先对数组的头部元素进行删除,使得尾部剩余的元素单调递增。这就需要一个指针初始指向尾部,如果只想的元素大于前一个元素,指针前移,直到指向的元素小于前一个元素为止,这就保证了从该指针到尾部的元素时单调递增的。将该指针前的元素全部删除,便可使得 arr 中剩下的元素是非递减的。
但该结果明显不是最好的,最佳的答案可能是删除中间的一段。例如数组 [1,2,3,10,4,2,3,5],最佳答案是删除中间的 [3,10,4] 或者 [10,4,2],而不是删除头部的[1,2,3,10,4] 。

假设删除的是 arr[i+1]∼arr[j−1]之间的元素,我们需要保证:

	1. arr[0]∼arr[i]非递减。
	2. arr[j]∼arr[n−1]非递减。
	3. arr[i]≤arr[j]。

如果我们枚举 i 和 j,然后再用 O(n)的时间判断是否满足上述条件,那么总复杂度是 O(n^3 )。但如果我们在从对i进行从小到大枚举的过程中,也从大到小枚举j,那么总复杂度将会降低至O(n^2)。
假设对于当前的 i 来说,j1​ 是最优的,那么这意味着这个j1是最小的满足条件 2 和条件 3 的下标。最小是因为我们要使得被删除数组 arr[i+1]∼arr[j−1] 最短。此时我们将 i 加 1,如果满足条件 1,即 arr[i+1]≥arr[i],那么i+1 所匹配的 j2​ 满足 j2≥j1​。
因此,我们需要两个指针来维护这样的 i 和 j。实现流程如下:

🍓实现流程
1️⃣ 得到数组大小n,数组最后一个元素下标为n-1;
2️⃣ 得到指针j,使得j到数组末端的元素都是单调递增的。初始化答案为 j 前面的元素个数
3️⃣ 从0开始枚举i,直到n-1。对于每个i,若arr[i]>arr[j],j后退,直到 arr[j]≥arr[i] 或者 j=n。
4️⃣ 此时 j−i−1 就是我们要删除的元素个数,用它来更新答案。然后令 i 等于 i+1,并保证 arr[i+1]≥arr[i],如果不满足则直接跳出循环,如果满足则继续下一轮枚举。
🌻过程模拟
在这里插入图片描述复杂度分析:
⏳时间复杂度 O(N) :其中 n 是 arr的长度。双指针 i 和 j 移动过程中,每个元素最多只会被遍历两次,所以复杂度为 O(n)。
🏠空间复杂度 O(1) :数据流长度为N,两个堆一个存入N个数据。

🔑3、代码:
class Solution {
public:
    int findLengthOfShortestSubarray(vector<int>& arr) {
        int n = arr.size(), j = n - 1;
        while(j>0&&arr[j-1]<=arr[j]) j--;
        if(j==0) return 0;
        int res=j;
        for(int i=0;i<n;i++){
            while(j<n &&arr[j]<arr[i]) j++; 
            res=min(res,j-i-1); 
            if(i+1<n &&arr[i]>arr[i+1]){
                break;
            }
        }
        return res;
    }
};

坚持打卡,继续加油!✊✊

博主水平有限,如果有疑问的可以评论区留言交流!
​🚀🚀更新不易,觉得文章写得不错的小伙伴们,点赞评论关注走一波💕💕~~~谢谢啦🙏 🙏🙌 !

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

君莫笑lucky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值