581. 最短无序连续子数组
给你一个整数数组 nums ,你需要找出一个连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
请你找出符合题意的 最短 子数组,并输出它的长度。
要求时间复杂度为O(n).
思路
左边最长上升子数组的最大值都小于右边上升子数组的最小值。
分别从左往右和从右往左遍历,找出左右两边失效的位置,则这两个失序位置中间的数组即为需要排序的最短子数组。
关于这里的失序:
1. 从左向右遍历,当前元素小于已遍历元素的最大值,此时的位置为最右边的失序位置;继续遍历,不断更新失序位置;
2. 从右向左遍历,当前元素大于已遍历元素的最小值,此时的位置为最左边的失序位置;继续遍历,不断更新失序位置;
3. 最右边失序位置减去最左边失序位置即为需要重排的最少个数。
代码实现
class Solution {
public int findUnsortedSubarray(int[] arr) {
int len = arr.length;
if(len == 0 || len == 1) return 0;
//左边的最大值
int maxLeft = arr[0];
//右边的最小值
int minRight = arr[len-1];
//从左向右遍历过程中维护的最右失序位置
int InvalidRightPos = 0;
//从右向左遍历过程中维护的最左失效位置
int InvalidLeftPos = 1;
for(int i = 1; i < len; i++) {
//当前遍历到的元素小于已遍历元素中的最大值,这个位置即为最右失序位置
if(maxLeft > arr[i]) {
InvalidRightPos = i;
}else { //当前遍历到的元素大于等于已遍历元素中的最大值,更新左部最大值,为当前元素值
maxLeft = arr[i];
}
}
for(int i = arr.length - 2; i >= 0; i--){
//当前遍历到的元素大于已遍历元素中的最小值,这个位置即为最左失序位置
if(minRight < arr[i]){
InvalidLeftPos = i;
}else{ //当前遍历到的元素小于已遍历元素中的最大值,更新左部最小值,为当前元素值
minRight = arr[i];
}
}
//如果有序,返回应该为0,所以初始化InvalidRightPos、InvalidLeftPos这两个变量时要保证数组有序时返回为0.
return InvalidRightPos - InvalidLeftPos + 1;
}
}