zoukankan      html  css  js  c++  java
  • [LC] Binary Search 题目/方法总结

    Binary search 是最简单也十分常用的一种算法,能把O(n) 的搜索降到O(logn)这个帖子的总结是参考大神Grandyang的,甚至很多地方是直接抄袭过来,原贴在这里http://www.cnblogs.com/grandyang/p/6854825.html

    Binary Search Template

    二分法常见关键词:sorted;

    二分常见痛点有以下几个:

    • 循环条件:start + 1 < end
    • mid = start + (end - start) /2;
    • A[mid] ==, < > 
    • return A[start] or A[end] 

    第一境界:寻找target

    这个level其实就是直接套用模板,一般就是在sorted array里面找target/insertion;

    例题: https://leetcode.com/problems/binary-search/description/

     1     public int search(int[] A, int target) {
     2         int s = 0, e = A.length - 1;
     3         while (s + 1 < e){
     4             int mid = s + (e - s) / 2;
     5             if (A[mid] == target){
     6                 return mid;
     7             }else if (A[mid] < target){
     8                 s = mid + 1;
     9             }else{
    10                 e = mid - 1;
    11             }
    12         }
    13         
    14         if (A[s] == target) return s;
    15         if (A[e] == target) return e;
    16         return -1;
    17     }

    例题:https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/description/

    这个题目非常直接,直接考察找两个postion,也是十分简单,就是套用模板;可以采取套用模板两次,找到first,last;也可以先找到first,然后把指针往后移找到last;

    也可以先o(logn)找到first,然后在first,end直接二分o(logm)找到last;这里选用了第二种方法,o(logn) 找到first,然后平移pointer去找last,worst case O(n);

     1     public int[] searchRange(int[] A, int target) {
     2         if (A.length == 0) return new int[]{-1, -1};
     3         
     4         int s = 0, e = A.length - 1;
     5         while (s + 1 < e){
     6             int mid = s + (e - s)/ 2;
     7             if (A[mid] >= target){
     8                 e = mid;
     9             }else{
    10                 s = mid + 1;
    11             }
    12         }
    13         
    14         int first = -1;
    15         if (A[s] == target){
    16             first = s;
    17         }else if (A[e] == target){
    18             first = e;
    19         }else{
    20             return new int[]{-1, -1};
    21         }
    22         
    23         int last = first;
    24         while (last + 1 < A.length && A[last + 1] == target){
    25             last ++;
    26         }
    27         
    28         return new int[]{first, last};
    29

    第二境界:寻找OOXX,即满足条件的最后/第一 OOOOOOO...OOXX….XXXXXX

    这个level 大部分是找满足条件的一个或者最后一个,即分界处,相关常见关键词:first、last、rotated sorted、peak etc

    例题:https://leetcode.com/problems/first-bad-version/description/

    太简单就不贴代码了;

    Search in rotated sorted array

    例题:https://leetcode.com/problems/search-in-rotated-sorted-array/description/

    这个题目就是画图,找到几种情况start, end该怎么变就行,主要关注的是A[end] and A[mid] 的关系。

     1     public int search(int[] nums, int target) {
     2         if (nums == null || nums.length == 0){
     3             return -1;
     4         }
     5         
     6         int start = 0, 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             }else if (nums[mid] < target){
    12                 if (nums[end] >= nums[mid] && target > nums[end]){
    13                     end = mid;
    14                 }else{
    15                     start = mid;
    16                 }
    17             }else{
    18                 if (nums[end] < nums[mid] && target <= nums[end]){
    19                     start = mid;
    20                 }else{
    21                     end = mid;
    22                 }
    23             }
    24         }
    25         
    26         if (nums[start] == target){
    27             return start;
    28         }else if (nums[end] == target){
    29             return end;
    30         }
    31         
    32         return -1;
    33     }

    例题:https://leetcode.com/problems/search-in-rotated-sorted-array-ii/description/

    是上面那题的变形,要注意的是有duplicate的出现,那么我们A[start] == A[end]成为可能,这样我们变换end的时候,只能让end --, 不能直接end = mid;

     

     1     public boolean search(int[] A, int target) {
     2         if (A.length == 0) return false; 
     3         
     4         int start = 0, end = A.length - 1, mid;
     5 
     6         while (start + 1 < end){
     7             mid = start + (end - start) / 2;
     8             System.out.println("mid=" + mid);
     9             if (A[mid] == target) return true;
    10             if (A[mid] > target){
    11                 if (target <= A[end] && A[mid] > A[end]){
    12                     start = mid;
    13                 }else{
    14                     end = (A[mid] < A[end]) ? mid : end - 1;
    15                 }
    16             }else{ // A[mid] < target
    17                 if (target > A[end] && A[mid] <= A[end]){
    18                     end = (A[mid] < A[end]) ? mid : end - 1;
    19                 }else{
    20                     start = mid;
    21                 }
    22             }
    23         }
    24         
    25         if (A[start] == target || A[end] == target) return true;
    26         return false;
    27     }

    例题:https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/

    这可以转化成找第一个数where A[i] < A[i + 1];,我们可以令target = A[end];这样转化成找到first value that less than target;

     1     public int findMin(int[] A) {
     2         // change to find the first value that less than the A[end];
     3         int s = 0, e = A.length - 1, target = A[e];
     4         while (s + 1 < e){
     5             int mid = s + (e - s) / 2;
     6             if (A[mid] >= target){
     7                 s = mid;
     8             }else{
     9                 e = mid;
    10             }
    11         }
    12         
    13         return A[s] <= target ? A[s] : A[e];
    14         
    15     }

    https://leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii/description/

    上一题的拓展,主要不同就是有duplicate;和search in rotated array 一样,有duplicate的时候我们不能简单end = mid或者start = mid,而是s++ 或者e++,想象一下这个case[3,1,3,3,3], 如果我们看到A[mid] >= target(3); 就赋值-> start = mid,这样会丢失掉1这个值,所以我们要的是start ++;

        public int findMin(int[] A) {
            // change to find the first value that less than the A[end];
            int s = 0, e = A.length - 1, target = A[e];
            while (s + 1 < e){
                int mid = s + (e - s) / 2;
                if (A[mid] >= target){
                    s ++;
                }else{
                    e = mid;
                }
            }
            
            return A[s] <= target ? A[s] : A[e];
            
        }

    自己找higher bound

    例题:https://leetcode.com/problems/search-in-a-sorted-array-of-unknown-size/description/

    size 不知道,也即是没有end,那我们就用一个e做expoenital increase直到找到array中大于target,就可以赋值为这个higher bound;

     1     public int search(ArrayReader reader, int target) {
     2         int e = 1;
     3         while (reader.get(e) < target){
     4             e <<= 1;
     5         }
     6         
     7         int s = e >> 1;
     8         while (s + 1 < e){
     9             int mid = s + (e - s) / 2;
    10             if (reader.get(mid) == target){
    11                 return mid;
    12             }else if (reader.get(mid) < target){
    13                 s = mid + 1;
    14             }else{
    15                 e = mid - 1;
    16             }
    17         }
    18         
    19         if (reader.get(s) == target){
    20             return s;
    21         }else if (reader.get(e) == target){
    22             return e;
    23         }
    24         
    25         return -1;
    26     }

     

     应用实例

    例题:https://leetcode.com/problems/heaters/description/

    这个题目可以对每个房子找到它在heater中的位置,然后求两个最小值,然后对所有房子这个最小值求最大值即可;

    其实这个题不需要用binary search;用two-pointer其实更好

     1 class Solution {
     2     public int findRadius(int[] houses, int[] heaters) {
     3         int max = 0;
     4         Arrays.sort(heaters);
     5         
     6         // for each house, find its position in heater;
     7         for (int h : houses){
     8             //int min = find(heaters, h);
     9             //System.out.println("h=" + h + " min=" + min);
    10             max = Math.max(max, find(heaters, h));
    11         }
    12         
    13         return max;
    14     }
    15     
    16     private int find(int[] A, int h){
    17         int s = 0, e = A.length - 1;
    18         while (s + 1 < e){
    19             int mid = s + (e - s) / 2;
    20             if (A[mid] == h){
    21                 return 0;
    22             }else if (A[mid] < h){
    23                 s = mid;
    24             }else{
    25                 e = mid;
    26             }
    27         }
    28         
    29         return Math.min(Math.abs(h - A[s]), Math.abs(A[e] - h));
    30     }
    31 }

     two pointer:

     1 class Solution {
     2     public int findRadius(int[] houses, int[] heaters) {
     3         Arrays.sort(houses);
     4         Arrays.sort(heaters);
     5         
     6         int j = 0, max = 0;
     7         for (int i = 0; i < houses.length; i ++){
     8             // when the next heaters is closer to the current house, we move pointer to next heater
     9             while (j + 1 < heaters.length && 
    10                    Math.abs(heaters[j] - houses[i]) >= Math.abs(heaters[j+1] - houses[i])){
    11                 j ++;
    12             }
    13             
    14             max = Math.max(max, Math.abs(heaters[j] - houses[i]));
    15         }
    16         
    17         return max;
    18     }
    19 }

     例题: https://leetcode.com/problems/arranging-coins/description/

    这是一个典型的二分数学问题,利用sum = (1 + end) * end / 2 和 n的关系我们可以看出是找最后一个满足条件的位置,二分典型oooxxx问题;

     1     public int arrangeCoins(int n) {
     2         long s = 0, e = n;
     3         long nlong = (long) n;
     4         
     5         while (s + 1 < e){
     6             long mid = s + (e - s) / 2;
     7             
     8             // expected sum = (1 + mid) * mid / 2;
     9             if ( (1 + mid) * mid  / 2 <= nlong){
    10                 s = mid;
    11             }else{
    12                 e = mid - 1;
    13             }
    14         }
    15         
    16         if ((1 + e) * e / 2 <= nlong) return (int) e;
    17         if ((1 + s) * s / 2 <= nlong) return (int) s;
    18         return 0;
    19     }

    相似例题: 

    https://leetcode.com/problems/valid-perfect-square/description/

    https://leetcode.com/problems/valid-triangle-number/description/ 

    https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/description/

    第三境界:去掉一半

    这一类博主还没有掌握也是最头疼的一类,只能通过多做题目来积累经验了;

     

  • 相关阅读:
    BZOJ3670:[NOI2014]动物园(KMP)
    415. [HAOI2009] 旅行
    U10223 Cx大帝远征埃及
    U10206 Cx的治疗
    2741. [济南集训 2017] 掰巧克力
    复习题目汇总 over
    7-20 表达式转换(25 分)
    7-19 求链式线性表的倒数第K项(20 分)(单链表定义与尾插法)
    7-18 银行业务队列简单模拟(25 分)
    7-17 汉诺塔的非递归实现(25 分)(有待改进)
  • 原文地址:https://www.cnblogs.com/timoBlog/p/9582201.html
Copyright © 2011-2022 走看看