• 二分查找


    有人说,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/

  • 相关阅读:
    Java反射学习笔记:getParameterTypes和getGenericParameterTypes区别
    面试官:如何实现一个LruCache,原理是什么?
    Android EventBus源码分析,基于最新3.1.1版本,看这一篇就够了!!
    EventBus 使用(全面分析,细节提醒)
    Arrays.toList() 和Collections.singletonList()的区别
    opencv绘制图和文字
    opencv图像的膨胀与腐蚀
    opencv图像处理形态学操作
    opencv提取水平和垂直线
    opencv图像金字塔上采样和降采样
  • 原文地址:https://www.cnblogs.com/isakovsky/p/14059913.html
Copyright © 2020-2023  润新知