第2题 力扣LeetCode 热题 HOT 100 (581. 最短无序连续子数组)
题目
给你一个整数数组 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
提示:
1 <= nums.length <= 104
-105 <= nums[i] <= 105
**进阶:**你可以设计一个时间复杂度为 O(n)
的解决方案吗?
题解
思路 求出需要进行排序子数组的开始下标和末尾下标,结果就是子数组末尾下标减去子数组开始下标加上一。
例如示例一, 只要对子数组[6,4,8,10, 9]进行排序,即求出子数组开始下标 即 数字6对应原数组的下标1和末尾下标9对应的原数组下标5,结果就是 (5-1+1 = 5)
一个原数组可以按顺序分为3段
- 符合原数组要求的子数组(已经排好序,并且位置符合要求)
- 一段未排序的子数组
- 符合原数组要求的子数组(已经排好序,并且位置符合要求)
以示例1 [2,6,4,8,10,9,15]举例
第1段为 [2]
第2段为 [6,4,8,10,9]
第3段为[15]
如果第2段长度为0,即不存在,得出原数组都是已经排好序,即不用再对子数组进行排序,返回长度0。
如果第2段长度不为0,求出第2段的起始位置下标和结尾下标,返回结尾下标减去起始下标加上一。
先求子数组的起始下标,子数组左边的值都为已经排序好的另一个子数组(第一段),即左边的值都为升序排序,维护两个值,一个值表示子数组最左边的值和下标,如果遍历原数组时,有小于之前保存的子数组数组最左边的值,更新最左边的值,并且以之前子数组最左边值的下标开始在原数组中试探能不能再往前走。
以示例1为例,设置得第一个左数组值为4,下标为2。它能往前走,移动到下标为1的位置。更新最左边的值和最左边的下标。
同理求出子数组的结尾下标。
/**
* @param {number[]} nums
* @return {number}
*/
var findUnsortedSubarray = function(nums) {
//初始化为-1,标志最左边和最右边的下标不存在。
let left = {index: -1};
let right = {index: -1};
// 标志type1=0 时,标志最左边的下标还不存在。 type1=1时,标志着已经存在最左边的下标。
let type1 = 0;
let type2 = 0;
for(let i = 1; i< nums.length; i++){
if(nums[i]<nums[i-1] && (type1==0 || nums[i]<left.value)){
type1 = 1;
//更新子数组最左边的值
left.value = nums[i];
//子数组最左边的下标存在吗? 存在的话,从最左边的下标开始向前找,不存在即从当前下标-1的位置开始找
let index = left.index==-1?i-1: left.index;
for(let j=index; j>=0; j--){
if(nums[j]> nums[i]){
//如果第一段还存在某个值小于当前下标的值,更新子数组最左边下标的值。
left.index = j;
}
}
}
}
for(let i = nums.length-2; i>=0; i--) {
if(nums[i]>nums[i+1] && (type2==0 || nums[i]>right.value)){
type2 = 1;
right.value = nums[i];
let index = right.index==-1?i+1: left.index;
for(let j=index; j< nums.length; j++){
if(nums[j]<nums[i]){
right.index = j;
}
}
}
}
// 如果子数组最左边下标或最右边下标不存在,返回子数组长度0
if(left.index==-1 || right.index==-1) return 0;
let result = right.index - left.index+1;
// 如果存在返回 子数组最左边下标减去最右边下标加一,即为子数组长度
return result;
};