题目链接: https://leetcode.com/problems/shortest-unsorted-continuous-subarray/description/
Description
Given an integer array, you need to find one continuous subarray that if you only sort this subarray in ascending order, then the whole array will be sorted in ascending order, too.
You need to find the shortest such subarray and output its length.
Example 1:
Input: [2, 6, 4, 8, 10, 9, 15]
Output: 5
Explanation: You need to sort [6, 4, 8, 10, 9] in ascending order to make the whole array sorted in ascending order.
Note:
- Then length of the input array is in range [1, 10,000].
- The input array may contain duplicates, so ascending order here means <=.
解题思路
方法一:
先从数组两侧往中间分别找第一个无序的位置,即第一个下标 i
满足 nums[i] > nums[i + 1]
和第一个下标 j
满足 nums[j - 1] > nums[j]
。可以确定区间 [i, j]
是目标区间的一个子集,因为在区间 [i, j]
中可能有比 [0, i - 1]
区间内某个数更小的数和比 [j + 1, n]
区间内某个数更大的数。所以,我们要遍历区间 [i, j]
,找到最小值 minNum
和最大值 maxNum
,然后向两侧扩充,往左找到第一个小于等于 minNum
的元素值下标 i
,往右找到第一个大于等于 maxNum
的元素值下标 j
,此时区间 [i + 1, j - 1]
即为目标区间。
方法二:
使用栈 s
来维持一个递增或递减序列的下标,区间的上下界分别用 start
和 end
记录。
先从左往右遍历一次数组 nums
以确定目标区间的下界,对每一个元素 nums[i]
,比较栈顶对应的数组元素 nums[s.top()]
是否大于 nums[i]
,若大于则更新 start = min(start, s.top())
并出栈,重复直到栈为空或 nums[s.top()] <= nums[i]
,将当前下标 i
入栈。这样遍历完一次后,start
的值就变成目标区间的上界了。
在对数组从右往左遍历一次,用类似的方法可以确定目标区间的下界 end
。
举个例子,nums = [2, 6, 4, 8, 10, 9, 15]
。:
后面为循环比较后的栈内元素,最左边为栈底,
- 从左往右遍历
- 2:
[0]
- 6:
[0, 1]
- 4:
[0, 2]
,更新start = 1
- 8:
[0, 2, 3]
- 10:
[0, 2, 3, 4]
- 9:
[0, 2, 3, 5]
,4 大于 1,start
值不变 - 15:
[0, 2, 3, 5, 6]
- 2:
- 从右往左遍历
- 15:
[6]
- 9:
[6, 5]
- 10:
[6, 4]
,更新end = 5
- 8:
[6, 4, 3]
- 4:
[6, 4, 3, 2]
- 6:
[6, 4, 3, 1]
,2 小于 5,end
值不变 - 2:
[6, 4, 3, 1, 0]
- 15:
目标区间为 [1, 5]
。
Code
方法一:
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
// 从两端向内找到第一个无序的位置
int i = 0, j = nums.size() - 1;
while (i < nums.size() - 1 && nums[i] <= nums[i + 1])
i++;
while (j > 0 && nums[j - 1] <= nums[j])
j--;
if (i >= j) return 0;
// 在 nums[i..j] 中找最小值 minNum 和最大值 maxNum
int minNum = INT_MAX, maxNum = INT_MIN;
for (int k = i; k <= j; k++) {
minNum = min(minNum, nums[k]);
maxNum = max(maxNum, nums[k]);
}
// 为确保 nums[0...i - 1] <= minNum 和 nums[j + 1...n] >= maxNum 向外扩展
while (i >= 1 && nums[i - 1] > minNum)
i--;
while (j < nums.size() - 1 && nums[j + 1] < maxNum)
j++;
return j - i + 1;
}
};
方法二:
class Solution {
public:
int findUnsortedSubarray(vector<int> &nums) {
int start = nums.size(), end = 0;
stack<int> s;
for (int i = 0; i < nums.size(); ++i) {
while (!s.empty() && nums[i] < nums[s.top()]) {
start = min(start, s.top());
s.pop();
}
s.push(i);
}
while (!s.empty()) s.pop();
for (int i = nums.size() - 1; i >= 0; --i) {
while (!s.empty() && nums[i] > nums[s.top()]) {
end = max(end, s.top());
s.pop();
}
s.push(i);
}
return start < end ? end - start + 1 : 0;
}
};