題目:給定一個無序數組 arr,求出需要排序的最短子數組長度。例如:arr = [1,5,3,4,2,6,7] 返回 4,因爲只有 [5,3,4,2] 需要排序。
要求:時間複雜度 O(N)、額外空間複雜度O(1)。
假設數組爲 [a b c d e f g h i j k l m n],如果 abc 是有序的,mn是 有序的,至於中間的 defghijkl 是無序的,我們可以得知,如果是正常升序序列,左邊的一定是小於右邊的任意數值,右邊的一定大於左邊的任意數值。
分析:
分別從左往右和從右往左遍歷,找出左右兩邊失效的位置,則這兩個失效位置中間的數組即爲需要排序的最短子數組。
步驟:
1、從左向右遍歷,找出不合適數的最右範圍:從左往右遍歷,如果 maxLeft > 當前元素,則記錄它的位置到 invalidRight ,一直遍歷到最右邊【可知 invalidRight 是最後一個不滿足排序要求的數,其右邊都滿足大於 invalidRight 】
2、從右向左遍歷,找出不合適數的最左範圍:從右往左遍歷,如果當前元素 > minRight,則記錄它的位置爲 invalidLeft,一直遍歷到最左邊 【可知 invalidLeft 是最後一個不滿足排序要求的數,其左邊都滿足小於 minRight】
3、invalidRight - invalidLeft + 1 就是需要排序的最短子數組長度。
public class GetMinLengthForSort {
public static int getMinLengthForSort(int[] arr){
if(arr == null || arr.length < 2){
return 0; // 不需要排序
}
int maxLeft = arr[0]; // 左邊最大
int minRight = arr[arr.length - 1]; // 右邊最小
// 這兩個指針分別記錄左右兩邊無效的位置
int invalidLeft = 0;
int invalidRight = -1; // 數組原本有序時:invalidRight - invalidLeft + 1 = 0
// 1、從左到右遍歷:找出不合適數的最右範圍
// 遍歷過的最大值大於當前值,那麼當前值肯定是無效的,那麼排序時這個最大值在當前位置或者是更右的位置
for(int i = 1; i < arr.length; i++){
if(maxLeft > arr[i]){
// 如果已經遍歷過的最大值大於當前值,則記錄最右邊無效位置
invalidRight = i;
}else{
// 如果已經遍歷過的最大值小於等於當前值,則遍歷過的最大值更新爲當前值
maxLeft = arr[i];
}
}
// 2、從右向左遍歷:找出不合適數的最左範圍
// 遍歷過的最小值小於當前值,那麼當前值就是無效的,那麼排序時這個最小值在當前位置或者是更左的位置
for(int i = arr.length - 2; i >= 0; i--){
if(minRight < arr[i]){
// 如果已經遍歷的最小值小於當前值,則記錄最左邊無效的位置
invalidLeft = i;
}else{
// 更新最小值
minRight = arr[i];
}
}
// invalidRight是不合適數的最右範圍,invalidLeft 是不合適數的最左範圍
// invalidRight - invalidLeft + 1 就是不合適數的個數
return invalidRight - invalidLeft + 1;
}
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(getMinLengthForSort(arr));
}
}