1. 程式人生 > >[LeetCode] Shortest Unsorted Continuous Subarray 最短無序連續子陣列

[LeetCode] Shortest Unsorted Continuous Subarray 最短無序連續子陣列

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:

  1. Then length of the input array is in range [1, 10,000].
  2. The input array may contain duplicates, so ascending order here means <=.

這道題給了我們一個數組,讓我們求最短的無序連續子陣列,根據題目中的例子也不難分析出來是讓我們找出陣列中的無序的部分。那麼我最開始的想法就是要確定無序子陣列的起始和結束位置,這樣就能知道子陣列的長度了。所以我們用一個變數start來記錄起始位置,然後我們開始遍歷陣列,當我們發現某個數字比其前面的數字要小的時候,說明此時陣列不再有序,所以我們要將此數字向前移動,移到其應該在的地方,我們用另一個變數j來記錄移動到的位置,然後我們考慮要不要用這個位置來更新start的值,當start還是初始值-1時,肯定要更新,因為這是出現的第一個無序的地方,還有就是如果當前位置小於start也要更新,這說明此時的無序陣列比之前的更長了。我們舉個例子來說明,比如陣列[1,3,5,4,2],第一個無序的地方是數字4,它移動到的正確位置是座標2,此時start更新為2,然後下一個無序的地方是數字2,它的正確位置是座標1,所以此時start應更新為1,這樣每次用i - start + 1來更新結果res時才能得到正確的結果,參見程式碼如下:

解法一:

class Solution {
public:
    int findUnsortedSubarray(vector<int>& nums) {
        int res = 0, start = -1, n = nums.size();
        for (int i = 1; i < n; ++i) {
            if (nums[i] < nums[i - 1]) {
                int j = i;
                while (j > 0 && nums[j] < nums[j - 1
]) { swap(nums[j], nums[j - 1]); --j; } if (start == -1 || start > j) start = j; res = max(res, i - start + 1); } } return res; } };

下面這種方法是用了一個輔助陣列,我們新一個跟原陣列一摸一樣的陣列,然後排序。從陣列起始位置開始,兩個陣列相互比較,當對應位置數字不同的時候停止,同理再從末尾開始,對應位置上比較,也是遇到不同的數字時停止,這樣中間一段就是最短無序連續子陣列了,參見程式碼如下:

解法二:

class Solution {
public:
    int findUnsortedSubarray(vector<int>& nums) {
        int n = nums.size(), i = 0, j = n - 1;
        vector<int> t = nums;
        sort(t.begin(), t.end());
        while (i < n && nums[i] == t[i]) ++i;
        while (j > i && nums[j] == t[j]) --j;
        return j - i + 1;
    }
};

下面這種方法很叼啊,是O(n)的時間複雜度加上O(1)的空間複雜度,博主覺得這實際上是對上面的那種方法進行空間上的優化的結果,用兩個變數mx和mn來代替上面的有序陣列,我們仔細來分析發現,最小值mn初始化為陣列的最後一個數字,最大值mx初始化為了第一個數字,然後我們從第二個數字開始遍歷,mx和nums[i]之間取較大值賦值給mx,然後比較此時mx和nums[i]之間的大小關係,如果mx大於nums[i],就把i賦值給end,那麼我們想如果第一個數字小於第二個,mx就會賦值為第二個數字,這時候mx和nums[i]就相等了,不進行任何操作,這make sense,因為說明此時是有序的。mn和nums[n-1-i]之間取較小值賦給mn,然後比較此時mn和nums[n-1-i]之間的大小關係,如果mn小於nums[n-1-i],就把n-1-i賦值給start,那麼什麼時候會進行賦值呢,是當倒數第二個數字大於最後一個數字,這樣mn還是最後一個數字,而nums[n-1-i]就會大於mn,這樣我們更新start。我們可以看出start是不斷往前走的,end是不斷往後走的,整個遍歷完成後,start和end就分別指向了最短無序連續子陣列的起始和結束位置,參見程式碼如下:

解法三:

class Solution {
public:
    int findUnsortedSubarray(vector<int>& nums) {
        int n = nums.size(), start = -1, end = -2;
        int mn = nums[n - 1], mx = nums[0];
        for (int i = 1; i < n; ++i) {
            mx = max(mx, nums[i]);
            mn = min(mn, nums[n - 1 - i]);
            if (mx > nums[i]) end = i;
            if (mn < nums[n - 1 - i]) start = n - 1 - i;
        }
        return end - start + 1;
    }
};

參考資料: