题目描述:
给你一个整数数组
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
解题思路:
方法一:双指针(贪心思想,最优)
思路:
数组可以分成三段:有序前缀 + 无序中段 + 有序后缀。
-
左边界 :从左往右扫描,维护左边最大值
maxNum。如果nums[i] < maxNum,说明i在无序区域内,更新low = i。 -
右边界 :从右往左扫描,维护右边最小值
minNum。如果nums[i] > minNum,说明i在无序区域内,更新high = i。 -
结果 :
low - high + 1就是最短无序子数组的长度。
代码实现:
cpp
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
int n = nums.size();
if (n <= 1) return 0;
int low = 0, high = n - 1;
int maxNum = nums[0], minNum = nums[n - 1];
for (int i = 1; i < n; i++) {
// 从左往右:维护最大值,找最右边的乱序位置
maxNum = max(maxNum, nums[i]);
if (nums[i] < maxNum) low = i;
// 从右往左:维护最小值,找最左边的乱序位置
minNum = min(minNum, nums[n - 1 - i]);
if (nums[n - 1 - i] > minNum) high = n - 1 - i;
}
return low > high ? low - high + 1 : 0;
}
};
复杂度分析:
| 维度 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 一次遍历 |
| 空间复杂度 | O(1) | 只用常数个变量 |
优缺点:
| 优点 | 缺点 |
|---|---|
| ✅ 时间复杂度 O(n),最优 | ❌ 思路不直观,需要理解"乱序"的判断 |
| ✅ 空间复杂度 O(1) | ❌ 边界条件容易写错(如 low > high 的判断) |
| ✅ 一次遍历同时找左右边界 | |
| ✅ 面试首选 |
方法二:排序 + 双指针
思路:
-
把原数组复制一份并排序。
-
对比排序后的数组和原数组:
-
从左往右找到第一个不同的位置
l -
从右往左找到最后一个不同的位置
r
-
-
r - l + 1就是最短无序子数组的长度。
代码实现:
cpp
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
vector<int> sorted = nums;
sort(sorted.begin(), sorted.end());
int l = 0, r = nums.size() - 1;
while (l <= r && nums[l] == sorted[l]) l++;
while (l <= r && nums[r] == sorted[r]) r--;
return r - l + 1;
}
};
复杂度分析:
| 维度 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n log n) | 排序 |
| 空间复杂度 | O(n) | 复制一份数组 |
优缺点:
| 优点 | 缺点 |
|---|---|
| ✅ 思路直观,容易理解 | ❌ 时间复杂度 O(n log n),不是最优 |
| ✅ 代码简洁,不易出错 | ❌ 需要额外 O(n) 空间 |
| ✅ 适合快速写出答案 | ❌ 面试官可能追问"能否 O(n)" |
两种方法对比:
| 对比维度 | 双指针 + 线性扫描 | 排序 + 双指针 |
|---|---|---|
| 时间复杂度 | O(n) | O(n log n) |
| 空间复杂度 | O(1) | O(n) |
| 代码复杂度 | 中等 | 简单 |
| 是否修改原数组 | ❌ 不修改 | ❌ 不修改 |