二分法 Binary Search


二分法還是比較常見和簡單的,之前也遇到過一些二分的相關題目,雖然不難,但是每次都需要在邊界問題上諸多考慮,今天聽了九章算法的課程,學習到一種方法使得邊界問題簡單化。

二分法的幾個注意點:

1. mid = start + (end - start) / 2;//特定情況下,避免越界。

2.循環控制條件:start + 1 < end,這樣處理的好處是可以避免使用start<end或者start<=end對於不同情況的不同處理邏輯。

3.理解二分法的三個層次:

  • 保留首位兩個指針,取重點,判斷往哪兒走
  • 保留下來的那一半是需要的那一半
  • 滿足某個條件的第一個/最后一個位置

這里給出二分的一個模板代碼:

這是求解數組中出現target的任意位置均可

 1 public int findPosition(int[] nums, int target) {
 2         // Write your code here
 3         if(nums == null || nums.length == 0)
 4             return -1;
 5         int start = 0;
 6         int end = nums.length-1;
 7         while(start + 1 < end){
 8             int mid = start + (end - start) / 2;
 9             if(nums[mid] == target){
10                 return mid;
11             }
12             else if(nums[mid] < target){
13                 start = mid;
14             }
15             else{
16                 end = mid;
17             }
18         }
19         if(nums[start] == target)
20             return start;
21         if(nums[end] == target)
22             return end;
23         return -1;
24     }
View Code

假如需要返回第一個位置,那么需要處理的就只是9-11行,在nums[mid] == target 的時候,end=mid;//這里需要注意,不是start=mid

假如需要返回最后一個出現的位置,那么需要在nums[mid] == target 的時候 start = mid,並且在循環結束之后也需要先判斷end時候滿足條件,然后再判斷start時候滿足條件。

二分法——二分位置,一般是給一個數組,然后讓你給出第一個/最后一個滿足XX條件的位置,畫圖有利於分析。

接下來看幾個二分的例子。

 

搜索二維矩陣

給了一個二維矩陣滿足條件:

 

  • 每行中的整數從左到右是排序的。
  • 每行的第一個數大於上一行的最后一個整數。

要搜索某個目標值target的位置。

方法一:使用兩次二分,第一次找可能在的行,需要尋找行首最后一次小於等於target的行;然后再找到的行內查看是否包含target

 1 public boolean searchMatrix(int[][] matrix, int target) {
 2         // write your code here
 3         //find the last row <= target
 4         if(matrix == null || matrix.length == 0)
 5             return false;
 6         int start = 0;
 7         int end = matrix.length - 1;
 8         while( start + 1 < end){
 9             int mid = start + (end - start) / 2;
10             if( matrix[mid][0] == target){
11                 return true;
12             }
13             else if( matrix[mid][0] < target){
14                 start = mid;
15             }
16             else{
17                 end = mid;
18             }
19         }
20         int row=0;
21         if(matrix[end][0] <= target)
22             row = end;
23         else
24             row = start;
25         start = 0;
26         end = matrix[0].length - 1;
27         while(start + 1 < end){
28             int mid = start + (end - start) / 2;
29             if(matrix[row][mid] == target)
30                 return true;
31             else if(matrix[row][mid] < target){
32                 start = mid;
33             }
34             else{
35                 end = mid;
36             }
37         }
38         if(matrix[row][start] == target || matrix[row][end] == target)
39             return true;
40         return false;
41     }
View Code

方法二:也可以把整個二維數組統一為一個一維的數組,start=0 end=m*n-1,然后二分。需要注意row=mid/n col=mid%n

 1  public boolean searchMatrix(int[][] matrix, int target) {
 2         // write your code here
 3         if(matrix == null || matrix.length == 0)
 4             return false;
 5         if(matrix[0] == null || matrix[0].length == 0)
 6             return false;
 7         int m = matrix.length;
 8         int n = matrix[0].length;
 9         int start = 0;
10         int end = m * n - 1;
11         while(start + 1 < end){
12             int mid = start + (end - start) / 2;
13             int row = mid / n;
14             int col = mid % n;
15             if(matrix[row][col] == target)
16                 return true;
17             else if(matrix[row][col] < target){
18                 start = mid;
19             }
20             else{
21                 end = mid;
22             }
23         }
24         if(matrix[start / n][start % n] == target || matrix[end / n][end % n] == target)
25             return true;
26         return false;
27     }
View Code

 

搜索二維矩陣 II

 

寫出一個高效的算法來搜索m×n矩陣中的值,返回這個值出現的次數。

這個矩陣具有以下特性:

 

  • 每行中的整數從左到右是排序的。
  • 每一列的整數從上到下是排序的。
  • 在每一行或每一列中沒有重復的整數。

18 19行可以去除,因為每行每列都沒有重復的數

 1 public int searchMatrix(int[][] matrix, int target) {
 2         // write your code here
 3         if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
 4             return 0;
 5         }
 6         int num = 0;
 7         int row = 0;
 8         int col = matrix[0].length - 1;
 9         while (row < matrix.length && col >= 0 ) {
10             if (matrix[row][col] > target) {
11                 col--;
12             }
13             else if (matrix[row][col] < target) {
14                 row++;
15             }
16             else {
17                 num++;
18                 num +=findRow(matrix, target, row, col - 1);
19                 num +=findCol(matrix, target, row + 1, col);
20                 row++;
21                 col--;
22             }
23         }
24         return num;
25     }
26     public int findRow(int[][] matrix, int target, int row, int col) {
27         int start = 0;
28         int end = col;
29         while (start + 1 < end) {
30             int mid = start + (end - start) / 2;
31             if (matrix[row][mid] == target) {
32                 end = mid;
33             }
34             else {
35                 start = mid;
36             }
37         }
38         if (matrix[row][start] == target) {
39             return col - start + 1;
40         }
41         else if (matrix[row][end] == target) {
42             return col - end + 1;
43         }
44         else {
45             return 0;
46         }
47     }
48     public int findCol(int[][] matrix, int target, int row, int col) {
49         int start = row;
50         int end = matrix.length - 1;
51         while(start + 1 < end) {
52             int mid = start + (end - start) /2;
53             if (matrix[mid][col] == target) {
54                 start = mid;
55             }
56             else {
57                 end = mid;
58             }
59         }
60         if(matrix[end][col] == target) {
61             return end - row +1;
62         }
63         else if(matrix[start][col] == target) {
64             return end - row + 1;
65         }
66         else {
67             return 0;
68         }
69     }
View Code

 

搜索插入位置

 給定一個排序數組和一個目標值,如果在數組中找到目標值則返回索引。如果沒有,返回到它將會被按順序插入的位置。

你可以假設在數組中無重復元素。

其實就是尋找第一個大於等於target的元素位置,不存在這樣的元素的話就放到最后。

 1  public int searchInsert(int[] A, int target) {
 2         // write your code here
 3         //分析題目可知,其實就是尋找第一個大於等於target的位置
 4         if(A == null || A.length == 0)
 5             return 0;
 6         int start = 0;
 7         int end = A.length - 1;
 8         while(start + 1 < end){
 9             int mid = start + (end - start) / 2;
10             if(A[mid] == target){
11                 end = mid;
12             }
13             else if(A[mid] < target){
14                 start = mid;
15             }
16             else{
17                 end = mid;
18             }
19         }
20         if(A[start] >= target)
21             return start;
22         if(A[end] >= target)
23             return end;
24         return A.length;
25     }
View Code

 

在大數組中查找 

給一個按照升序排序的正整數數組。這個數組很大以至於你只能通過固定的接口 ArrayReader.get(k) 來訪問第k個數。(或者C++里是ArrayReader->get(k)),並且你也沒有辦法得知這個數組有多大。找到給出的整數target第一次出現的位置。你的算法需要在O(logk)的時間復雜度內完成,k為target第一次出現的位置的下標。

如果找不到target,返回-1。

倍增。這里不能直接使用二分,因為不知道上限在哪里,所以首先用倍增尋找到一個上限,然后再二分。

 1 public int searchBigSortedArray(ArrayReader reader, int target) {
 2         // write your code here
 3         int k = 1;
 4         while(reader.get(k - 1) < target){
 5                 k *= 2;
 6         }
 7         int start = 0;
 8         int end = k - 1;
 9         while(start + 1 < end){
10             int mid = start + (end - start) / 2;
11             if(reader.get(mid) == target){
12                 end = mid;
13             }
14             else if(reader.get(mid) < target){
15                 start = mid;
16             }
17             else{
18                 end = mid;
19             }
20         }
21         if(reader.get(start) == target)
22             return start;
23         if(reader.get(end) == target)
24             return end;
25         return -1;
26     }
View Code

 

尋找旋轉排序數組中的最小值

假設一個旋轉排序的數組其起始位置是未知的(比如0 1 2 4 5 6 7 可能變成是4 5 6 7 0 1 2)。

你需要找到其中最小的元素。

你可以假設數組中不存在重復的元素。

畫出圖來就比較好分析了。主要判斷mid在前一段還是后一段。使用最后一個元素與nums[mid]的大小關系來判斷mid是位於前一段還是后一段。注意不能使用第一個元素來划分,因為這樣需要單獨討論沒有rotate的情況。注意使用這種方法是因為數組中沒有重復元素的。否則最壞情況O(n),直接遍歷得了。

 1  public int findMin(int[] num) {
 2         // write your code here
 3         if ( num == null || num.length == 0) {
 4             return -1;
 5         }
 6         int start = 0;
 7         int end = num.length - 1;
 8         int divide = num[end];
 9         while (start + 1 < end) {
10             int mid = start + (end - start) / 2;
11             if (num[mid] == divide) {
12                 end = mid;
13             }
14             else if (num[mid] < divide) {
15                 end = mid;
16             }
17             else{
18                 start = mid;
19             }
20         }
21         return Math.min(num[start],num[end]);
22     }
View Code

 

尋找旋轉排序數組中的最小值 II

有重復元素的時候,例如22221222,就不能向之前的直接處理,這里的方法是先把前邊與end相等的剔除掉。

3133 3313

 1 public int findMin(int[] num) {
 2         // write your code here
 3         if ( num == null || num.length == 0) {
 4             return -1;
 5         }
 6         int start = 0;
 7         int end = num.length - 1;
 8         while (start < end && num[start] == num[end]) {
 9             start ++;
10         }
11         while (start + 1 < end) {
12             int mid = start + (end - start) / 2;
13             if (num[mid] == num[end]) {
14                 end = mid;
15             }
16             else if (num[mid] < num[end]) {
17                 end = mid;
18             }
19             else{
20                 start = mid;
21             }
22         }
23         return Math.min(num[start],num[end]);
24     }
View Code

 

搜索旋轉排序數組

假設有一個排序的按未知的旋轉軸旋轉的數組(比如,0 1 2 4 5 6 7 可能成為4 5 6 7 0 1 2)。給定一個目標值進行搜索,如果在數組中找到目標值返回數組中的索引位置,否則返回-1。

你可以假設數組中不存在重復的元素。

首先根據nums[start]與nums[mid]的大小關系,可以判斷位於前半段還是后半段,然后再根據target的大小關系判斷位於mid之前還是之后。

 1  public int search(int[] A, int target) {
 2         // write your code here
 3         if(A == null || A.length == 0)
 4             return -1;
 5         int start = 0;
 6         int end = A.length - 1;
 7         while(start + 1 < end){
 8             int mid = start + (end - start) / 2;
 9             if(A[mid] == target){
10                 return mid;
11             }
12             if(A[start] < A[mid]){
13                 if(A[start] <= target && target <= A[mid]){
14                     end = mid;
15                 }
16                 else
17                     start = mid;
18             }
19             else{
20                 if(A[mid] <=target && target <= A[end]){
21                     start = mid;
22                 }
23                 else
24                     end = mid;
25             }
26         }
27         if(A[start] == target)
28             return start;
29         if(A[end] == target)
30             return end;
31         return -1;
32     }
View Code

 

搜索旋轉排序數組II

13111     1131

也是允許有重復元素的情況。注意一個關鍵的地方不僅僅開頭等結尾的剔除掉,而且判斷在前半段還是后半段還需要加上nums[start]<=nums[mid]。這里等號不能略去。

 1 public boolean search(int[] A, int target) {
 2         // write your code here
 3         if (A == null || A.length == 0) {
 4             return false;
 5         }
 6         int start = 0;
 7         int end = A.length - 1;
 8         while (start < end && A[start] == A[end]) {
 9             start++;
10         }
11         while (start + 1 < end) {
12             int mid = start + (end - start) / 2;
13             if (A[mid] == target) {
14                 return true;
15             }
16             if (A[start] <= A[mid]) { // 2 2 2 3 1等號放在這種情況
17                 if (A[start] <= target && target <= A[mid]) {
18                     end = mid;
19                 }
20                 else {
21                     start = mid;
22                 }
23             }
24             else {
25                 if (A[mid] <= target && target <= A[end]) {
26                     start = mid;
27                 }
28                 else {
29                     end = mid;
30                 }
31             }
32         }
33         return A[start] == target || A[end] == target;
34     }
View Code

 

 

 

二分法——二分答案

這些情況下沒有給出一個數組讓你二分,但同樣也是希望找出滿足某個條件的最大值最小值。同樣看幾個例子。

 

x的平方根

其實就是尋找最后一個平方小於等於x的數。OOOXXX

 1  public int sqrt(int x) {
 2         // write your code here
 3         long start = 0;
 4         long end = x;
 5         while (start + 1 < end) {
 6             long mid = start + (end - start) / 2;
 7             if (mid * mid == x)
 8                 return (int)mid;
 9             else if (mid * mid < x){
10                 start = mid;
11             }
12             else{
13                 end = mid;
14             }
15         }
16         if (end * end <= x)
17             return (int)end;
18         return (int)start;
19     }
View Code

 

第一個錯誤的代碼版本

代碼庫的版本號是從 1 到 n 的整數。某一天,有人提交了錯誤版本的代碼,因此造成自身及之后版本的代碼在單元測試中均出錯。請找出第一個錯誤的版本號。

你可以通過 isBadVersion 的接口來判斷版本號 version 是否在單元測試中出錯

其實就是找到第一個壞的版本。

 1  public int findFirstBadVersion(int n) {
 2         // write your code here
 3         int start = 1;
 4         int end = n;
 5         while (start + 1 <end) {
 6             int mid = start + (end - start) / 2;
 7             if (SVNRepo.isBadVersion(mid)) {
 8                 end = mid;
 9             }
10             else {
11                 start = mid;
12             }
13         }
14         if(SVNRepo.isBadVersion(start)){
15             return start;
16         }
17         return end;
18     }
View Code

 

木材加工

有一些原木,現在想把這些木頭切割成一些長度相同的小段木頭,需要得到的小段的數目至少為 k。當然,我們希望得到的小段越長越好,你需要計算能夠得到的小段木頭的最大長度。

其實就是尋找最后一個滿足條件的數,從1-max(木頭長度),這里的條件就是用這個長度切出來的木頭的數量大於等於k

 1 public int woodCut(int[] L, int k) {
 2         // write your code here
 3         if (L == null || L.length == 0) {
 4             return 0;
 5         }
 6         int start = 1;
 7         int end = Integer.MIN_VALUE;
 8         for (int i : L){
 9             end =Math.max(i,end);
10         }
11         while(start + 1 < end){
12             int mid = start + (end - start) / 2;
13             if (getWoodNum(L,mid) >= k) {
14                 start = mid;
15             }
16             else{
17                 end = mid;
18             }
19         }
20         if (getWoodNum(L,end) >= k) {
21             return end;
22         }
23         else if(getWoodNum(L,start) >= k){
24             return start;
25         }
26         return 0;
27     }
28     public int getWoodNum(int[] L,int len){
29         int result=0;
30         for(int i : L) {
31             result += i / len;
32         }
33         return result;
34     }
View Code

 

尋找峰值*

你給出一個整數數組(size為n),其具有以下特點:

  • 相鄰位置的數字是不同的
  • A[0] < A[1] 並且 A[n - 2] > A[n - 1]

假定P是峰值的位置則滿足A[P] > A[P-1]A[P] > A[P+1],返回數組中任意一個峰值的位置。

首先,分析題目可知,假如我遍歷一遍數組,那么肯定可以找到,並且可以找到所有的,復雜度為O(n),那么我這里思考更高的效率,自然往log(n)上思考,自然想到二分。

然后,峰值點必然位於1 到 len - 2。畫出圖形來方便分析。nums[mid] <nums[mid-1]表明位於下降的一段,end=mid; nums[mid]<nums[mid+1]表明位於上升的一段start=mid;上述都不成立則mid就是一個峰值點。

 1 public int findPeak(int[] A) {
 2         // write your code here
 3         if (A == null || A.length < 3)
 4             return -1;
 5         int start = 1;
 6         int end = A.length - 2;
 7         while ( start + 1 < end) {
 8             int mid = start + (end - start) / 2;
 9             if (A[mid] < A[mid - 1]) {
10                end = mid; 
11             }
12             else if (A[mid] < A[mid + 1]) {
13                 start = mid;
14             }
15             else{
16                 return mid;
17             }
18         }
19         if (A[start] < A[end]){
20             return end;
21         }
22         return start;
23     }
View Code

 

在排序數組中找最接近的K個數

給一個目標數 target, 一個非負整數 k, 一個按照升序排列的數組 A。在A中找與target最接近的k個整數。返回這k個數並按照與target的接近程度從小到大排序,如果接近程度相當,那么小的數排在前面。

 1 public int[] kClosestNumbers(int[] A, int target, int k) {
 2         // Write your code here
 3         if (A == null || A.length == 0) {
 4             return new int[1];
 5         }
 6         int[] result = new int[k];
 7         int start = 0;
 8         int end = A.length - 1;
 9         while (start + 1 < end) {
10             int mid = start + (end - start) / 2;
11             if (A[mid] == target) {
12                 end = mid;
13             }
14             else if (A[mid] < target) {
15                 start = mid;
16             }
17             else {
18                 end = mid;
19             }
20         }
21         int index = 0;
22         while (index < k) {
23             if (start >=0 && end < A.length) {
24                 if (Math.abs(A[start] - target) <= Math.abs(A[end] - target)) {
25                     result[index++] = A[start--];
26                 }
27                 else {
28                     result[index++] = A[end++];
29                 }
30             }
31             else if(start >= 0){
32                 result[index++] = A[start--];
33             }
34             else {
35                  result[index++] = A[end++];
36             }
37         }
38         return result;
39     }
View Code

 Median of Two Sorted Arrays

求兩個排序數組的中位數

思路一:歸並排序合並的思想。num1最后為第medium大的數,num2最后為第medium + 1大的數。復雜度為O(m+n)

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if (nums1 == null || nums2 == null) {
            return 0.0;
        }
        int m = nums1.length;
        int n = nums2.length;
        int medium = (m + n) / 2;
        int i = 0; 
        int j = 0;
        int k = 1;
        int num1 = 0;
        int num2 = 0;
        while (i < m || j < n) {
            if (i < m && j < n) {
                if (nums1[i] < nums2[j]) {
                    num1 = num2;
                    num2 = nums1[i++];
                    k++;
                } else {
                    num1 = num2;
                    num2 = nums2[j++];
                    k++;
                }
            } else if (i < m) {
                num1 = num2;
                num2 = nums1[i++];
                k++;
            } else {
                num1 = num2;
                num2 = nums2[j++];
                k++;
            }
            if (k > medium + 1) {
                break;
            }
        }
        if (((m + n) % 2) == 0) {
            return (num1 + num2) / 2.0;
        } else {
            return (double)num2;
        }
    }
View Code

 

思路二:使用二分。需要得到topK的元素,那么看兩個數組在topK/2的元素,哪個小,這個之前的就一定不會是topK的元素,直接去除即可。這樣復雜度為O(log(m + n))。基准情況為某個數組開始位置已經越界或者開始位置加上topK/2后越界,或者topK==1。

注意:topK == 1的base情況容易忘記。

 public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int length = nums1.length + nums2.length;
        if (length % 2 == 1) {
            return helper(nums1, 0, nums2, 0, length / 2 + 1);
        }
        else {
            return (helper(nums1, 0, nums2, 0, length / 2 + 1) + helper(nums1, 0, nums2, 0, length / 2)) / 2.0;
        }
    }
    public int helper(int[] nums1, int start1, int[] nums2, int start2, int topK) {
        if (start1 >= nums1.length) {
            return nums2[start2 + topK - 1];
        }
        if (start2 >= nums2.length) {
            return nums1[start1 + topK - 1];
        }
        
        if (topK == 1) {
            return Math.min(nums1[start1], nums2[start2]);
        }
        
        if (start1 + topK/2 - 1 >= nums1.length) {
            return helper(nums1, start1, nums2, start2 + topK/2, topK - topK/2);
        }
        if (start2 + topK/2 - 1 >= nums2.length) {
            return helper(nums1, start1 + topK/2, nums2, start2, topK - topK/2);
        }
       
       
        if (nums1[start1 + topK/2 - 1] < nums2[start2 + topK/2 - 1]) {
            return helper(nums1, start1 + topK/2, nums2, start2, topK - topK/2);
        }
        else {
            return helper(nums1, start1, nums2, start2 + topK/2, topK - topK/2);
        }
    }
View Code

  Find the Duplicate Number

一個大小為n+1的數組,只能存放1~n的整數,現在假設只存在重復元素,並且這個元素可以出現不止兩次,找出這個整數。

 1 def findDuplicate(self, nums):
 2         """
 3         :type nums: List[int]
 4         :rtype: int
 5         """
 6         nums.sort()
 7         length = len(nums)
 8         start = 0
 9         end = length - 1
10         while start + 1 < end:
11             mid = (start + end) / 2
12             if nums[mid] < mid + 1:
13                 end = mid
14             else:
15                 start = mid
16         return start if nums[start] < start + 1 else end
View Code

 

Kth Smallest Element in a sorted Matrix

方法一:使用優先隊列,首先優先隊列中存儲入第一行的前k個元素,然后彈出一個,進來一個,進行k-1次,隊列中最小的的即為第k個元素。進來的元素為彈出的那個元素的下一行的元素。

 1 public class Solution {
 2     public int kthSmallest(int[][] matrix, int k) {
 3         int n=matrix.length;
 4         PriorityQueue<Tuple> pq=new PriorityQueue<Tuple>();
 5         int bound=Math.min(k,n);
 6         for(int j=0;j<bound;j++){
 7             pq.add(new Tuple(matrix[0][j],0,j));
 8         }
 9         for(int i=1;i<k;i++){
10             Tuple t=pq.poll();
11             int x=t.x+1;
12             if(x==n)
13                 continue;
14             pq.offer(new Tuple(matrix[x][t.y],x,t.y));
15         }
16         return pq.poll().val;
17     }
18 }
19 class Tuple implements Comparable<Tuple>{
20     int x;
21     int y;
22     int val;
23     public Tuple(int val,int x,int y){
24         this.x=x;
25         this.y=y;
26         this.val=val;
27     }
28     @Override
29     public int compareTo(Tuple that){
30         return this.val-that.val;
31     }
32 }
View Code

 

方法二:二分。初始化start為matrix[0][0],end為matrix[-1][-1]。然后對於mid,分別計算每一行的比mid小的元素個數,加起來如果小於k,表明start =mid,否則end = mid。python的bisect模塊對這種操作提供了很好地支持。

 1 class Solution(object):
 2     def kthSmallest(self, matrix, k):
 3         lo, hi = matrix[0][0], matrix[-1][-1]
 4         while lo<hi:
 5             mid = (lo+hi)//2
 6             if sum(bisect.bisect_right(row, mid) for row in matrix) < k:
 7                 lo = mid+1
 8             else:
 9                 hi = mid
10         return lo
View Code

 

Drop Eggs

http://datagenetics.com/blog/july22012/index.html

網址blog分析得太好了,看得我很激動。

這里大致總結一下文中的意思。

首先,只有兩個蛋,假設我們第一次丟選擇第m層。

  如果這個蛋壞了,那么我們就需要利用剩下的這個蛋從第一層開始往上檢測,最壞情況需要1,2,3,...m - 1,(因為此時你再不能隨意的去檢測了,不然這個蛋碎了就game over了),所以兩個蛋一共需要的丟的次數是m次。

  如果這個蛋沒壞,那么我們就需要往后檢測。這里作者首先引入一種方法是以固定間隔m來檢測那些樓層,比如:假設樓高100層,假設第一次檢測樓層m已經選定為10,那么,第一次檢測10,然后20,30,40,···,100.好,我們現在來看,假設這個蛋壞的最低樓層就是100,那么我們需要檢測的就是10,20,30,40,50,60,70,80,90,100,然后91,92,93,94,95,96,97,98,99.我們發現91-99的檢測和10層蛋壞了的時候1-9層的檢測是很相似的。但是在這種情況下,我們多檢測了前邊那些10,20,30,40,50,60,70,80,90,這就導致了這種情況下我們一共需要的檢測次數變為了19次。這好煩人。

怎么辦?

  接下來就是算法的關鍵所在。我們利用第一個但來把檢測區間分為一個一個的小區間,然后在這些小區間內利用第二個蛋來進行檢測。由於首先選擇的m限定了最少的檢測次數就是m次,剛剛說的平分區間的方式導致在后邊的樓層的檢測次數多了,那么有沒有一種方法可以保證后邊的區間的樓層的檢測次數也和第一個區間內檢測次數一樣多呢?這里采用不均分區間的方式。

思考為什么后邊的區間的檢測次數會多了,這是因為前邊的檢測已經浪費了次數,那么我就思考加入讓后邊的區間變短呢?

這樣來做:

首先檢測m層時候壞,壞了ok之間檢測1-m-1,一共m次。

然后,沒壞的話第二個區間我讓長度變為m - 1,因為我在檢測第一個區間浪費了一次機會,所以我這里就讓區間長度減一,保證這個區間內的所有樓層我的最少檢測次數也是m

以此類推,后邊的區間長度分別為m-2,m-3,m-4,....,1,這樣一來,為了保證所有的樓層都能夠檢測到那么這些區間加起來1 + 2 + ... +(m - 1) + m = (m*(m + 1))/2 >= n

 

這樣我們得到最后的表達式:(m*(m + 1))/2 >= n

m為第一個蛋第一次丟的樓層,也是最少需要丟的次數。

 

There is a building of n floors. If an egg drops from the k th floor or above, it will break. If it's dropped from any floor below, it will not break.

You're given two eggs, Find k while minimize the number of drops for the worst case. Return the number of drops in the worst case.

1  public int dropEggs(int n) {
2         long ans = 0;
3         for(int i = 1; ;i++) {
4             ans += i;
5             if (ans >= n) {
6                 return i;
7             }
8         }
9     }
View Code

 

Maximum Number in Mountain Sequence

Given a mountain sequence of n integers which increase firstly and then decrease, find the mountain top.

 1 public int mountainSequence(int[] nums) {
 2         // Write your code here
 3         if (nums == null || nums.length == 0) {
 4             return 0;
 5         }
 6         int start = 0;
 7         int end = nums.length - 1;
 8         while (start + 1 < end) {
 9             int mid = (start + end) / 2;
10             if (nums[mid] > nums[mid + 1]) {
11                 end = mid;
12             } else {
13                 start = mid;
14             }
15         }
16         return Math.max(nums[start], nums[end]);
17     }
View Code

 


免責聲明!

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



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