欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

如何写一个正确的二分法代码

程序员文章站 2024-03-20 17:33:22
...

二分原理简单,但是实现起来经常出错,下面一些经验很有用:

  1. 跨步移动(left=mid+1,right=mid-1)是惯例,跨步移动保证每次搜索范围至少缩小1。如果不跨步,不管left<=right还是left<right都可能死循环,除非能保证提前退出的情况下才可以不跨步移动。
  2. 跨步移动的前提下,假设搜索范围是闭区间[0,n-1],left退出范围是[0,n],right是[-1,n-1]
  3.  left<=right情况下,如果依靠循环退出,left刚好在right右边一个,left<right退出刚好两个是相等的。选择<还是<=主要是等一下思考一下codes在相等情况下要不要再走一遍。left<=right更常用一些
  4. 分的时候,重点考虑一下if里面等于的情况!!!

有了跨步移动left<=right两个惯例,二分不确定性降低很多,只要思考if的时候甩哪半if里面等于的情况就好,这取决于数据单调性、重复性、是否一定有解、退出范围等各种问题。在这两个惯例的前提下,二分代码简洁而且有深刻含义,具体如下:

变种一:lower_bound和upper_bound (https://blog.csdn.net/taoqick/article/details/23260157

  1. lower_bound,重复数据里大于等于target的第一个元素,插入target的第一个恰当位置:
     
    int searchFirstEqualOrLarger(int *arr, int n, int key)
    {
        int left=0, right=n-1;
        while(left<=right) 
        {
            int mid = left+(right-left)/2;
            if(arr[mid] >= key) 
                right = mid-1;
            else if (arr[mid] < key) 
                left = mid+1;
        }
        return left;
    }

     

  2. upper_bound,重复数据里大于target的第一个元素,插入target的最后一个恰当位置:
     
    int searchFirstLarger(int *arr, int n, int key)
    {
        int left=0, right=n-1;
        while(left<=right)
        {
            int mid = left+(right-left)/2;
            if(arr[mid] > key) 
                right = mid-1;
            else if (arr[mid] <= key) 
                left = mid+1;
        }
        return left;
    }

     

变种二:

  1. 找出第一个与key相等的元素
     
    int searchFirstEqual(int *arr, int n, int key)
    {
        int left = 0, right = n-1;
        while(left<=right)
        {
            int mid = left+(right-left)/2;
            if(arr[mid] >= key) 
                right = mid - 1;
            else if(arr[mid] < key) 
                left = mid + 1;
        }
        if( left < n && arr[left] == key) 
                return left;
        return -1;
    }

     

  2. 找出最后一个与key相等的元素
     
    int searchLastEqual(int *arr, int n, int key)
    {
        int left = 0, right = n-1;
        while(left<=right) {
            int mid = (left+right)/2;
            if(arr[mid] > key) 
                right = mid - 1;
            else if(arr[mid] <= key) 
                left = mid + 1; 
        }
        if( right>=0 && arr[right] == key) 
                return right;
        return -1;
    }

     

变种三:

  1. 查找最后一个等于或者小于key的元素
     
    int searchLastEqualOrSmaller(int *arr, int n, int key)
    {
        int left=0, right=n-1;
        while(left<=right) 
        {
            int mid = left+(right-left)/2;
            if(arr[m] > key) 
                 right = m-1;
            else if (arr[m] <= key) 
                 left = m+1;
        }
        return right;
    }

     

  2. 查找最后一个小于key的元素
    int searchLastSmaller(int *arr, int n, int key)
    {
        int left=0, right=n-1;
        while(left<=right) {
            int mid = left+(right-left)/2;
            if(arr[mid] >= key) 
                 right = mid-1;
            else if (arr[mid] < key) 
                 left = mid+1;
        }
        return right;
    }