二分查找

有人说,90%的程序员都手写不出正确的二分查找

没错,我就是那90%

c++的标准库里只提供了binary_search(),lower_bound(),upper_bound()三个函数,缺点就是,只能在数组或者vector这样的线性数据结构上二分

所以就需要整理一下二分的用法和代码

1,binary_search(arr[],arr[]+size,key,cmp)

功能:找到正好等于key的元素,成功则返回位置,否则返回数组末尾指针

注意,给出的最后一个参数是比较函数的指针,如果不给出,默认调用标准库中的less()函数,其实就是小于号。

这个是最好写的,不需要考虑左右边界或者失配的问题

(伪)代码:

int binary_search(arr[],arr[]+size,key,cmp) {
    int left = 0; 
    int right = nums.length - 1;
    while(left <= right) {
        int mid = (right + left) / 2;
        if(equal(arr[mid],key))
            return arr[]+mid; 
        else if (less(nums[mid],target))
            left = mid + 1;
        else 
            right = mid - 1;
        }
    return arr[]+size;
}

2,lower_bound(arr[],arr[]+size,key,cmp)

这个函数的功能是找出第一个大于等于key的值,可以简记为左边界,和上一个函数不同的是,除非key值大于全部数组中的值,才会返回arr[]+size,否则一定会返回一个有效值。

这个代码我写了n遍,查阅了m个博客,都挂了,最后发现,直接去看c++的手册它不香吗?

template <class ForwardIterator, class T>
  ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val)
{
  ForwardIterator it;
  iterator_traits<ForwardIterator>::difference_type count, step;
  count = distance(first,last);
  while (count>0)
  {
    it = first; step=count/2; advance (it,step);
    if (*it<val) {                 // or: if (comp(*it,val)), for version (2)
      first=++it;
      count-=step+1;
    }
    else count=step;
  }
  return first;
}

(伪)代码:

int lower_bound (int l, int r, int key) {
    int it;
    int count,step;
    count = r-l+1;
    while (count>0) {
        step=count/2;
        it=l+step;
        if (a[it]<key) {
            l=it+1;
            count-=step+1;
        } else count=step;
    }
    return l;
}

 

注意,这个函数就比上一个难写很多,比如left,right为什么要小于等于,最后要返回的是left还是right,等等

3,upper_bound(arr[],arr[]+size,key,cmp)

这个函数的功能是找出大于key的第一个值的位置,简称右边界

还是先查c++手册:

template <class ForwardIterator, class T>
  ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val)
{
  ForwardIterator it;
  iterator_traits<ForwardIterator>::difference_type count, step;
  count = std::distance(first,last);
  while (count>0)
  {
    it = first; step=count/2; std::advance (it,step);
    if (!(val<*it))                 // or: if (!comp(val,*it)), for version (2)
      { first=++it; count-=step+1;  }
    else count=step;
  }
  return first;
}

然后照猫画虎,写出(伪)代码:

int upper_bound2 (int l,int r,int key) {
    int it;
    int count, step;
    count = r-l+1;
    while (count>0) {
        step=count/2;
        it=l+step;
        if ( a[it] <= key) {               
            l=it+1;
            count-=step+1;
        } else count=step;
    }
    return l;
}

 

这个函数常用于给整数开平方根或者类似的操作,因为大于key的第一个值,位置减去1,就是小于等于key的最后一个值

涉及到给整数开平方根或者类似的操作的,一定记得用二分,不要自作聪明求解析解,然后转换成double,在cmath库里的函数求解,再转换回来,绝对不要!

我是不会告诉你我被昨晚的atcoder arc的B题卡了一个多小时的,天知道,1e18的数据范围,他是怎么想到构造出卡掉double+sqrt的数据的

代码正确性验证:

洛谷P2249

arc109B

参考资料:

https://www.cnblogs.com/kyoner/p/11080078.html

http://www.cplusplus.com/reference/algorithm/

posted @ 2020-11-30 10:29  Isakovsky  阅读(114)  评论(0编辑  收藏  举报