LeetCode 新題: Find Minimum in Rotated Sorted Array 解題報告-二分法模板解法


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,直接返回左邊。

2. 中間斷開(也就是說旋轉過)
我們的目的是要找出存在斷口的地方。所以我們可以每次求一下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     }
View Code

 

SOLUTION 2:

采用九章算法的二分法模板來解:
這個模板最大的好處是 while(left < right - 1) 這樣的話,終止時就一定是left = right - 1,而且mid 一直會是在中間,不會靠到left去。判斷起來會相當
便利。
常見bug:

// 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     }
View Code
 
所以以后一定要多使用模板化的編程 ,特別是這里總結的二分法模板:
 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     }
View Code 

 

 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     }
View Code

FOLLOW UP:

LeetCode 新題: Find Minimum in Rotated Sorted Array II 解題報告-二分法模板解法

GitHub:

GitHub代碼鏈接


免責聲明!

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



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