[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 = 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;
    }
};

 

參考資料:

https://leetcode.com/problems/shortest-unsorted-continuous-subarray/

https://leetcode.com/problems/shortest-unsorted-continuous-subarray/discuss/103057/java-on-time-o1-space

https://leetcode.com/problems/shortest-unsorted-continuous-subarray/discuss/103062/c-clean-code-2-solution-sort-onlgn-max-min-vectors-on

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM