Find Minimum in Rotated Sorted Array

Question Solution
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
Find the minimum element.
You may assume no duplicate exists in the array.
SOLUTION 1:
這個題目trick的地方在於,它的旋轉pivot次數未知。所以有可能它轉了一圈又轉回了原處
所以我們代碼要處理2種情況:
我們還是用二分法來做。比起全部掃一次,二分法可以做到LogN的復雜度。
1. Sorted
這種情況簡單,先計算出mid的值,如果它處在左右的中間,證明這個數列是正常的sorted,直接返回左邊。
我們的目的是要找出存在斷口的地方。所以我們可以每次求一下mid的值,把mid 跟左邊比一下,如果是正常序,就丟掉左邊,反之丟掉右邊,不斷反復直到找到斷口。
分析一下:
比如4 5 6 7 0 1 2 從中間斷開后,它是由一個有序跟一個無序的序列組成的。
如果left = 0, right = 6,mid = 3, 那么4, 5, 6, 7 是正序, 7, 0, 1, 2是逆序,所以我們要丟掉左邊。這樣反復操作,直到數列中只有2個數字,就是斷開處,這題我們會得到7,0,返回后一個就可以了。
以下圖示簡單描述了通過三步操作逐步逼近斷口處。每一次我們可以扔掉一半,速度是LogN.
注意,丟掉半邊時,mid不可以丟掉,因為有可能mid就是答案。
例子: 3, 1, 2 的時候,3, 1是逆序,1, 2是正序,如果你扔掉1,2你就丟掉了答案。
1 // Solution 1: 2 public int findMin1(int[] num) { 3 if (num == null || num.length == 0) { 4 return 0; 5 } 6 7 if (num.length == 1) { 8 return num[0]; 9 } 10 11 12 // 至少有2個元素,才有討論的價值 13 int l = 0; 14 int r = num.length - 1; 15 16 while (l < r) { 17 int mid = l + (r - l)/2; 18 // Means that there is no rotate. 19 if (num[mid] >= num[l] && num[mid] <= num[r]) { 20 return num[0]; 21 } 22 23 // rotate > 0的情況 24 if (l == r - 1) { 25 // 當只余下2個元素的時候,這里是斷點,右邊的是小值 26 return num[r]; 27 } 28 29 if (num[mid] >= num[l]) { 30 // The left side is sorted. Discard left. 31 l = mid; 32 } else { 33 // The right side is sorted. 34 r = mid; 35 } 36 } 37 38 return 0; 39 }
SOLUTION 2:
采用九章算法的二分法模板來解:// bug 1: should not use l = m + 1 and r = m - 1.
// this may discard the minumul value.
A example: 3, 1, 2.
1 // solution 2: 2 public int findMin(int[] A) { 3 if (A == null || A.length == 0) { 4 return 0; 5 } 6 7 if (A.length == 1) { 8 return A[0]; 9 } else if (A.length == 2) { 10 return Math.min(A[0], A[1]); 11 } 12 13 // 至少有3個元素,才有討論的價值 14 int l = 0; 15 int r = A.length - 1; 16 17 while (l < r - 1) { 18 int m = l + (r - l) / 2; 19 20 // means that there is no rotate. 21 if (A[m] < A[r] && A[m] > A[l]) { 22 return A[0]; 23 } 24 25 // left side is sorted. 26 if (A[m] > A[l]) { 27 l = m; 28 } else { 29 r = m; 30 } 31 } 32 33 return A[r]; 34 }
1 while (l < r - 1) { 2 int m = l + (r - l) / 2; 3 4 // means that there is no rotate. 5 ... 這里添加各種退出條件,比如找到了目標值等 8 9 // left side is sorted. 10 if (A[m] > A[l]) { 11 l = m; 12 } else { 13 r = m; 14 } 15 }
SOLUTION 3:
在解答2的基礎之上,可以把判斷是否rotated的statement去掉,然后每次丟棄有序數列時,先丟棄右邊的。這樣子即使是從未rotated的也沒有關系,因為一直是丟右邊的
直到余下左邊最后2個。
1 public int findMin(int[] num) { 2 if (num == null || num.length == 0) { 3 return 0; 4 } 5 6 int l = 0; 7 int r = num.length - 1; 8 9 while (l < r - 1) { 10 int m = l + (r - l) / 2; 11 12 if (num[m] < num[r]) { 13 // bug 1: should not use l = m + 1 and r = m - 1. 14 // this may discard the minumul value. 15 r = m; 16 } else { 17 l = m; 18 } 19 } 20 21 return Math.min(num[l], num[r]); 22 }
SOLUTION 4:
在解答3的基礎之上先,預判斷是不是旋轉過,這樣如果沒有旋轉我們可以直接把解算出即可。
1 public int findMin(int[] num) { 2 if (num == null || num.length == 0) { 3 return 0; 4 } 5 6 int l = 0; 7 int r = num.length - 1; 8 9 if (num[l] < num[r]) { 10 return num[l]; 11 } 12 13 while (l < r - 1) { 14 int m = l + (r - l) / 2; 15 16 if (num[m] < num[r]) { 17 // bug 1: should not use l = m + 1 and r = m - 1. 18 // this may discard the minumul value. 19 r = m; 20 } else { 21 l = m; 22 } 23 } 24 25 return Math.min(num[l], num[r]); 26 }
FOLLOW UP:
LeetCode 新題: Find Minimum in Rotated Sorted Array II 解題報告-二分法模板解法
GitHub:
GitHub代碼鏈接