leetcode 581. 最短无序连续子数组(详解普通 / 进阶 / 单调栈解法,Java版)
題目
題解
方法1(暴力排序):時間復雜度O(nlogn),空間復雜度O(n)
一個簡單的想法是:將數組 nums 進行排序,記為 nums_sorted 。然后比較 nums 和 nums_sorted 的元素來決定最左邊和最右邊不匹配的元素。它們之間的子數組就是要求的最短無序子數組。
public int findUnsortedSubarray(int[] nums) {int[] snums = nums.clone();Arrays.sort(snums);int start = snums.length, end = 0;for (int i = 0; i < snums.length; i++) {if (snums[i] != nums[i]) {start = Math.min(start, i);end = Math.max(end, i);}}return (end - start >= 0 ? end - start + 1 : 0); }方法2(單調棧思想):時間復雜度O(n),空間復雜度O(n)
我們需要找到無序子數組中 最小元素 和 最大元素 分別對應的 正確位置,來求得我們想要的無序子數組的邊界。
為了達到這一目的,此方法中,我們使用 單調棧結構。
我們從頭遍歷 nums 數組,如果遇到的數字大小一直是升序的,我們就不斷把對應的下標壓入棧中,這么做是因為這些元素在目前都是 處于正確的位置上。一旦我們遇到 前面的數比后面的數大,也就是 nums[j] 比棧頂元素小,我們可以知道 nums[j] 一定不在正確的位置上。為了找到 nums[j] 的正確位置,我們 不斷將棧頂元素彈出,直到棧頂元素比 nums[j] 小,我們假設棧頂元素對應的下標為 k ,那么我們知道 nums[j] 的正確位置下標應該是 k+1 。
我們重復這一過程,直到遍歷完整個數組,這樣我們可以找到 最小的 k, 它就是 無序子數組的左邊界。
(實際上:所謂左邊界,就是 “所有未在正確位置的數當中最小的那個” 的正確位置,在數值上等同于 “所有的左邊界當中最小的左邊界”。同樣地,所謂右邊界,就是 “所有未在正確位置的數當中最大的那個” 的正確位置,在數值上等同于 “所有的右邊界當中最大的右邊界”。)
類似地,我們逆序遍歷一遍 nums 數組來找到無序子數組的 右邊界。這一次我們將降序的元素壓入棧中,如果遇到一個升序的元素,我們像上面所述的方法一樣不斷將棧頂元素彈出,直到找到一個更大的元素,以此找到無序子數組的右邊界。
我們可以看下圖作為參考。從圖中可以觀察到,上升還是下降決定了相對順序;還可以觀察到,指針 b 指向所有未在正確位置的數當中的最小的那個,它在下標 0 后面標記著 “無序子數組中最小的左邊界” 1;指針 a 指向所有未在正確位置的數當中的最大的那個,它在下標 7 前面標記著 “無序子數組中最大的右邊界” 6。
方法3(進階):時間復雜度O(n),空間復雜度O(1)
同時從前往后和從后往前遍歷,分別得到要排序數組的右邊界和左邊界;
尋找右邊界:
從前往后遍歷的過程中,用 max 記錄遍歷過的最大值,如果 max 大于當前的 nums[i],說明nums[i] 的位置不正確,屬于需要排序的數組,因此將右邊界更新為 i,然后更新 max;這樣最終可以找到需要排序的數組的右邊界,右邊界之后的元素都大于 max;
尋找左邊界:
從后往前遍歷的過程中,用 min 記錄遍歷過的最小值,如果 min 小于當前的 nums[j],說明 nums[j] 的位置不正確,應該屬于需要排序的數組,因此將左邊界更新為j,然后更新 min;這樣最終可以找到需要排序的數組的左邊界,左邊界之前的元素都小于 min;
注:從前往后遍歷和從后往前遍歷兩個過程可以分兩次循環完成,也可以放一起完成,這樣的話就有:j = len-i-1
public static int findUnsortedSubarray(int[] nums) {int len = nums.length;int max = nums[0]; // max記錄遍歷過的最大值int min = nums[len - 1]; // min記錄遍歷過的最小值int left = 0, right = -1; // 要排序數組的左邊界、右邊界for (int i = 0; i < len; i++) {// 從前往后遍歷if (max > nums[i]) {right = i;} else {max = nums[i];}// 從后往前遍歷if (min < nums[len - i - 1]) {left = len - i - 1;} else {min = nums[len - i - 1];}}return right - left + 1; }方法 3 執行用時:
總結
以上是生活随笔為你收集整理的leetcode 581. 最短无序连续子数组(详解普通 / 进阶 / 单调栈解法,Java版)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 左神算法:两个单链表相交的一系列问题(链
- 下一篇: 左神算法:将单链表的每K个节点之间逆序(