Algorithm - Binary Search/BiSection


https://hackernoon.com/binary-search-in-detail-914944a1434a
Given an integer x, find the maximum element y in an array of size N that satisfies the condition y <= x
We know that x might not be in the array. So the simple binary search for x in the given array won’t work. But for binary search, all we know is x, the key. We need to find the element in the array that’s the closest to x and less than or equal to x (if it exists).
  1. How to move low and high?
    When array[mid] < x , there is a possibility that the current element might be the answer (since y can be less than x).
    So, we shouldn’t discard mid while moving low.
    Hence, low becomes low = mid , not low = mid + 1
    High remains the same as it is irrelevant to our problem statement.
  2. How to compute mid?When we use mid = low + ((high - low) / 2) , we are calculating the lower mid. So when the #elements in the array is even, the 
    if(array[mid] > x) becomes false, so control will go to else clause where low = mid . This results in an infinite loop. (why? take an example)
    Hence, we take the higher mid, i.e., mid = low + ((high - low + 1) / 2)
  3. What would be the condition in while loop?Since we are storing the maximum element that is less than or equal to x in low, we should stop when low = high and return array[low] or array[high]. Hence, the condition in while loop is low < high .
int binarySearchExample1(int[] array, int key){
  int length = array.length;
  int low = 0;
  int high = length - 1;
  while(low < high){
      int mid = low + ((high - low + 1) / 2);
      int current = array[mid];
      if(current == key){
          return array[mid];
      }
      else if(current < key){
          low = mid;
      }
      else{
          high = mid - 1;
      }
  }
  return array[low];
}
Given an integer x, find the minimum element y in an array of size N that satisfies the condition y >= x


  1. low = mid + 1 for moving low
  2. high = mid for moving high
  3. mid = low + ((high - low) / 2) for calculating mid
  4. low < high for the condition in while loop
int binarySearchExample2(int[] array, int key){
  int length = array.length;
  int low = 0;
  int high = length - 1;
  while(low < high){
      int mid = low + ((high - low) / 2);
      int current = array[mid];
      if(current == key){
          return array[mid];
      }
      else if(current < key){
          low = mid + 1;
      }
      else{
          high = mid;
      }
  }
  return array[high];
}
lowhighmidwhile(?)
mid + 1mid - 1low + ((high - low) / 2)low <= high
mid + 1midlow + ((high - low) / 2)low < high
midmid - 1low + ((high - low + 1) / 2)low < high
midmidX (infinite loop)X (invalid)
https://juejin.im/entry/5b875108f265da436152fe8f


http://www.cnblogs.com/wuyuegb2312/archive/2013/05/26/3090369.html
loop invariant 循环不变式
1.二分查找值为key的下标,如果不存在返回-1。
循环不变式
  如果key存在于原始数组[0,n-1],那么它一定在[left,right]中。
int binsearch(int * array, int length, int key)
{
    if(!array)
        return -1;
    int left = 0, right = length,mid; // right -1?
    while(left <= right)
    {
        mid = left + (right-left)/2;
        if(array[mid] < key)
        {
            left = mid + 1;
        }else if(array[mid] > key)
        {
            right = mid - 1;
        }else
            return mid;
    }
    return -1;
}

2.二分查找返回key(可能有重复)第一次出现的下标x,如果不存在返回-1
循环不变式


  如果key存在于数组,那么key第一次出现的下标x一定在[left,right]中,且有array[left]<=key, array[right]>=key。
int binsearch_first(int * array, int length,int key)
{
    if(!array)
        return -1;
    int left = 0, right = length-1,mid;
    while(left < right)
    {
        mid = left + (right-left)/2;
        if(array[mid] < key)
            left = mid+1;
     else
            right = mid;
    }
    if(array[left] == key)
        return left;
    return -1;
}

3.二分查找返回key(可能有重复)最后一次出现的下标x,如果不存在返回-1(模仿2的第一版)
  与上一种不同,这个算法不能简单地根据对称,从上一个算法直接改过来,由于整数除法总是舍弃小数,mid有时会离left更近一些。所以这种算法只是沿着上一个算法思路的改进,看上去并不是很漂亮。

4.二分查找返回key(可能有重复)最后一次出现的下标x,如果不存在返回-1(修改版)
  根据3中的讨论,可以发现不能直接照搬的原因是mid=(left+right)/2的舍弃小数,在left+1==right且array[left]=key时,如果不加以人为干预会导致死循环。既然最终需要干预,干脆把需要干预的时机设置为终止条件就行了。
  使用while(left<right-1)可以保证每次循环时数组长度都会至少减一,终止时数组长度可能为2(left+1==right)、1(left==mid,上一次循环时right取mid==left),但是不可能为0。(每一次循环前总有left<=mid<=right,无论令left=mid还是令right=mid,都不会发生left>right)。同3一样,right总是指向数组中候选的最后一个可能为key的下标,此时只需先检查right后检查left是否为key就能确定x的位置。这样就说明了循环不变式的保持和终止,就不再形式化地写下来了。
  对于两种情况的合并:array[mid] == key时,mid有可能是x,不能将其排除;array[mid]<key时,如果让left = mid+1,不会违反循环不变式的条件。但是由上面的讨论可知,将left=mid也是可以的,在达到终止条件前能保证数组长度单调减少。因此把两种情况合并成最终形式。
int binsearch_last_v2(int * array, int length, int key)
{
    if(!array)    return -1;
    int left =0, right = length-1,mid;
    while(left < right -1)
    {
        mid = left + (right-left)/2;
        if(array[mid] <= key)
            left = mid;
        else
            right = mid;
    }

    if(array[right] == key)
        return right;
    else if(array[left] == key)
        return left;
    else
        return -1;
}

http://814193594.blog.51cto.com/10729329/1726286
//方法一,采用[]闭区间的方式
int BinarrySearch(int *arr, int len, int x)
{
     assert(arr);
     
     int left = 0;
     int right = len - 1;
   //---------------------
     int mid = 0;
     
     while (left <= right)
   //----------------------
     {
          mid = left + (right - left) / 2;
          if (x > arr[mid])
          {
               left = mid + 1;
          }
          else if (x < arr[mid])
          {
               right = mid - 1;
             //-----------------
          }
          else
          {
               return mid;
          }
     }
     return (-1);
}
//方法二,采用[ )左闭右开的方式
int BinarrySearch(int *arr, int len, int x)
{
     assert(arr);
     
     int left = 0;
     int right = len;
   //-----------------区别一
     int mid = 0;
     
     while (left < right)
   //---------------------区别二
     {
          mid = left + (right - left) / 2;
          if (x > arr[mid])
          {
               left = mid + 1;
          }
          else if (x < arr[mid])
          {
               right = mid;
             //-------------区别三
          }
          else
          {
               return mid;
          }
     }
     return (-1);
}

二分查找主要有三点需要注意:
1、边界问题
2、求中点时的溢出问题
3、有重复值时定位第一个
http://qianzui.github.io/blog/2013-03-15-how-to-correctly-write-binarysearch/
static int binarySearch(int[] array, int size, int value) {
int lo = 0;
int hi = size - 1;
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final int midVal = array[mid];
if (midVal < value) {
lo = mid + 1;
} else if (midVal > value) {
hi = mid - 1;
} else {
return mid; // value found
}
}
return ~lo; // value not present
}
http://hedengcheng.com/?p=595
java.util.Arrays.binarySearch0(long[], int, int, long)
private static int binarySearch0(long[] a, int fromIndex, int toIndex,
                                 long key) {
    int low = fromIndex;
    int high = toIndex - 1;

    while (low <= high) {
        int mid = (low + high) >>> 1;
        long midVal = a[mid];

        if (midVal < key)
            low = mid + 1;
        else if (midVal > key)
            high = mid - 1;
        else
            return mid; // key found
    }
    return -(low + 1);  // key not found.
}
lower_bound upper_bound binary_search的最佳写法
  1. public int lower_bound(int [] nums, int target){
  2. int l = 0, r = nums.length;//检索范围[l,r)
  3. while(l<r){
  4. int mid = l+(r-l)/2;
  5. if(nums[mid] > target) r = mid;
  6. else if(nums[mid] < target) l = mid+1;
  7. else r = mid;
  8. }
  9. return l;
  10. }
  11. public int upper_bound(int [] nums, int target){
  12. int l =0, r = nums.length;//检索范围[l,r)
  13. while(l<r){
  14. int mid = l+(r-l)/2;
  15. if(nums[mid] > target) r = mid;
  16. else if(nums[mid] <target) l = mid+1;
  17. else l = mid+1;
  18. }
  19. return r;
  20. }

https://www.zybuluo.com/rapiz/note/339250
http://blog.csdn.net/cannon0102/article/details/40113459
http://blog.csdn.net/u014688145/article/details/69094665
https://www.zhihu.com/question/36132386

http://www.geeksforgeeks.org/find-missing-number-geometric-progression/
Given an array that represents elements of geometric progression in order. One element is missing in the progression, find the missing number. It may be assumed that one term is always missing and the missing term is not first or last of series.
Simple Solution is to linearly traverse the array and find the missing number. Time complexity of this solution is O(n).
An efficient solution to solve this problem in O(Log n) time using Binary Search. The idea is to go to the middle element. Check if the ratio of middle and next to middle is equal to common ratio or not, if not then the missing element lies between mid and mid+1. If the middle element is equal to n/2th term in Geometric Series (Let n be the number of elements in input array), then missing element lies in right half. Else element lies in left half.
Note : Drawback withs this solution are : For larger values or for bigger array, it may cause overflow and/or may take more time to computer powers.

int findMissingRec(int arr[], int low,
                   int high, int ratio)
{
    if (low >= high)
        return INT_MAX;
    int mid = low + (high - low)/2;
    // If element next to mid is missing
    if (arr[mid+1]/arr[mid] != ratio)
        return (arr[mid] * ratio);
    // If element previous to mid is missing
    if ((mid > 0) && (arr[mid]/arr[mid-1]) != ratio)
        return (arr[mid-1] * ratio);
    // If missing element is in right half
    if (arr[mid] == arr[0] * (pow(ratio, mid)) )
        return findMissingRec(arr, mid+1, high, ratio);
    return findMissingRec(arr, low, mid-1, ratio);
}
// Find ration and calls findMissingRec
int findMissing(int arr[], int n)
{
    // Finding ration assuming that the missing term is
    // not first or last term of series.
    int ratio = (float) pow(arr[n-1]/arr[0], 1.0/n);
    return findMissingRec(arr, 0, n-1, ratio);
}


Xhttps://www.imooc.com/article/19963
  1. 差1错误。我们的左端点应该是当前可能区间的最小范围,那么右端点是最大范围呢,还是最大范围+1呢。我们取了中间值之后,在缩小区间时,有没有保持左右端点的这个假设的一致性呢?
  2. 死循环。我们做的是整数运算,整除2了之后,对于奇数和偶数的行为还不一样,很有可能有些情况下我们并没有减小取值范围,而形成死循环。
  3. 退出条件。到底什么时候我们才觉得我们找不到呢?

以下这个函数是二分查找nums中[left,right)部分,right值取不到,如果查到的话,返回所在地,如果查不到则返回最后一个小于target值得后一个位置。

//右值点不能取到的情况
    int binary_search(vector<int>& nums,int left,int right, int target) { 
    //坑点(1)right究竟能不能取到的问题,这里是不能取到的情况
        int i = left;
        int j= right;
        while(i<j){
            int mid = i+(j-i)/2;             //坑点(2)这里尽量这么写,因为如果写成(i+j)/2则有溢出的风险
            if(nums[mid]>=target)        //坑点(3)这个地方大于还是大于等于要依据情况而定
                j = mid;            //坑点(4)因为右值点反正不能取到,所以j就可以等于mid
            else
                i = mid+1;           //坑点(5)
        }
        return i;
    }

//右值点能取到的情况
    int searchInsert(vector<int>& nums,int left,int right, int target) {
        int i = left;
        int j= right;
        while(i<=j ){
            int mid = i+(j-i)/2;
            if(nums[mid]>=target)
                j = mid-1;
            else
                i = mid+1;
        }
        return i;
    }
一般来说写的二分查找的标准程序应该是右边right值取不到的情况,所以while循环中要加一步判断i是否小于等于nums.size();

对于坑点2,如果是右值点可以取到情况下,必须是i<=j否则会出现查找偏差,比如数组中就一个元素[3],让查找5的位置,理应返回1,但是此时会输出成0,
对于坑点3,主要应用在如果数组中有重复元素,需要判断其具体要返回什么位置。
对于坑点,5,如果i不加1的,比如i = 0,j= 1 的情况,会出现mid = 0然后一直死循环下去
对于坑点4,依据right能不能取到而定,如果right可以取到则,right必须要-1,不减1的话,还是会出现i = j时的死循环。
https://segmentfault.com/a/1190000011283470

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts