Question

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 <=.

問題解析:

給定整數陣列,找到一個最短的連續的未按照升序排序的最短子陣列,返回子陣列的長度。

Answer

Solution 1:

排序比較。

  • 一種較為直觀的想法,先將陣列排序賦給一個新的陣列,通過兩個陣列之間的比較,第一個和最後一個元素不同的位置索引即為子陣列的界限。
  • 這裡用Python來實現,先將陣列排序,後用zip(old, new)來比較排序前後陣列元素之間是否一一對應相等;
  • 陣列總長度減去正序的一個為False的位置和逆序第一個為False的位置。
class Solution(object):
    def findUnsortedSubarray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        sort_nums = sorted(nums)
        is_same = [a == b for a, b in zip(nums, sort_nums)]
        if all(is_same):
            return 0
        else:
            return len(nums) - is_same.index(False) - is_same[::-1].index(False)

Solution 2:

一般的比較方法。

  • 按照我們自己尋找最小子陣列的思路來解決。
  • 首先分別設定左、右指標來指示子陣列的首和尾,並設定最大值和最小值,(最小值賦大值,最大值賦小值);
  • 分別左右兩方向尋找不符合遞增規則的邊界索引,期間若左指標l最後等於陣列最大索引,則證明陣列全部元素均按升序排序;
  • 在找到的界限內尋找內部的最小值和最大值;
  • 左指標向左走,右指標向右走,直到小於界限內最小值後的位置,及大於界限內最大值前的位置。
  • 注意處理好最後返回值。
class Solution {
    public int findUnsortedSubarray(int[] nums) {
        int l = 0, r = nums.length - 1, min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;

        while (l < r && nums[l] <= nums[l+1]) l++;

        if (l == r) return 0;

        while (nums[r] >= nums[r-1]) r--;

        for (int k = l; k <= r; k++){
            max = Math.max(max, nums[k]);
            min = Math.min(min, nums[k]);
        }

        while (l >= 0 && min < nums[l]) l--;
        while (r <= nums.length-1 && max > nums[r]) r++;

        return r-l-1;

    }
}
  • 時間複雜度:O(n),空間複雜度:O(1)

Solution 3:

更簡潔的方法。

  • 一次遍歷,左、右同時進行;
  • 左邊前進記錄當前經過元素的最大值,若按照升序規則,則當前遍歷元素即為當前最大值;如果二者不相等,則用j記錄當前前進的索引;
  • 右邊後退記錄當前經過元素的最小值,按照升序規則,則當前遍歷元素即為當前最小值;如果二者不相等,則用i記錄當前後退的索引。
  • 當一次遍歷完成,前進的索引記錄了不符合升序規則的最大索引,後退的索引記錄了不符合規則的最小索引。
  • 注意在給ij賦初值的時候要考慮陣列元素全部按升序排序的情況,返回為0。所以,賦值ij為不大於0且相差1,如:i = 0, j = -1,或i = -1, j = -2
class Solution {
    public int findUnsortedSubarray(int[] nums) {
        int i = 0, j = -1, max = Integer.MIN_VALUE, min = Integer.MAX_VALUE;

        for (int l = 0, r = nums.length-1; r >= 0; l++, r--){
            max = Math.max(max, nums[l]);
            if (nums[l] != max) j = l;

            min = Math.min(min, nums[r]);
            if (nums[r] != min) i = r;
        }

        return (j - i + 1);
    }
}
  • 時間複雜度:O(n),空間複雜度:O(1)