• 二分法 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
  • 相关阅读:
    打印sql语句方法
    PHP实现innodb的数据回滚
    安装Sublime Text 3插件的方法
    Redis常用命令速查 <第二篇>
    本地配置环境打开项目出现404/本地wampserver配置伪静态以及php.ini配置
    linux的tar命令
    PHP无限极分类详谈
    PHP常用函数及其注释
    PHP常用到的功能函数
    【转】小菜硬件杂谈 细数主板上曾出现过的插槽
  • 原文地址:https://www.cnblogs.com/futurehau/p/5837891.html
Copyright © 2020-2023  润新知