[M双指针] lc581. 最短无序连续子数组(线性扫描+细节处理+算法优化)
Posted Ypuyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[M双指针] lc581. 最短无序连续子数组(线性扫描+细节处理+算法优化)相关的知识,希望对你有一定的参考价值。
1. 题目来源
2. 题目解析
不错的题目,两种做法。
方法一:暴力排序+找两侧端点不同点
- 直接排序,找到排序后与排序前左右两端第一个出现不同的地方,返回区间长度即可。
- 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
- 空间复杂度: O ( n ) O(n) O(n)
方法二:线性扫描
- 假设我们找到了答案区间,记区间端点为
[l, r]
,那么[0, l)
区间中一定是升序的,且nums[l-1]
一定小于[l, n)
区间的所有数。因为后面重排序后是要单调递增的。 - 同理,针对右区间端点也是同理。
nums[r+1]
是要大于[0, r]
之间的所有数的。 - 先将初始数组的升序部分先跳过,先得到一个
l, r
然后再分别找到[l+1, n)
的最小值mi
和[0, r-1]
的最大值mx
。 l
所处位置要比后半段最小值还要小,r
所处位置要不前半段最大值还要大。由于预先处理的l,r
已经单调递增,故直接找即可。- 最终,
l, r
将停在非法位置,答案区间长度即为r-l-1
。
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
代码:
方法一:直接排序+找不同点
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
int n = nums.size();
vector<int> q = nums;
sort(q.begin(), q.end());
int l = 0, r = n - 1;
while (l < n && nums[l] == q[l]) l ++ ;
while (r >= 0 && nums[r] == q[r]) r -- ;
if (l >= r) return 0;
return r - l + 1;
}
};
方法二:双指针+线性扫描
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
int n = nums.size();
int l = 0, r = n - 1;
while (l + 1 < n && nums[l + 1] >= nums[l]) l ++ ;
if (l == r) return 0;
while (r - 1 >= 0 && nums[r - 1] <= nums[r]) r -- ;
int mi = 1e9, mx = -1e9;
for (int i = l + 1; i < n; i ++ ) mi = min(mi, nums[i]);
for (int i = r - 1; i >= 0; i -- ) mx = max(mx, nums[i]);
while (l >= 0 && nums[l] > mi) l -- ; // 在此不应该用 >=
while (r < n && nums[r] < mx) r ++ ; // 在此不应该用 <=,不存在相邻情况
return r - l - 1;
}
};
以上是关于[M双指针] lc581. 最短无序连续子数组(线性扫描+细节处理+算法优化)的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode 581 最短无序连续子数组[排序] HERODING的LeetCode之路
LeetCode 581. 最短无序连续子数组(Shortest Unsorted Continuous Subarray)