惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
雷峰网
雷峰网
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
博客园 - Franky
腾讯CDC
T
Tailwind CSS Blog
Recent Announcements
Recent Announcements
V
V2EX
N
Netflix TechBlog - Medium
量子位
Jina AI
Jina AI
Y
Y Combinator Blog
The GitHub Blog
The GitHub Blog
G
Google Developers Blog
爱范儿
爱范儿
博客园 - 叶小钗
D
Docker
MongoDB | Blog
MongoDB | Blog
D
DataBreaches.Net
T
The Blog of Author Tim Ferriss

博客园 - 魔古力

2020年终总结 CCF CSP-J/S2020第二轮认证评级名单 CSP-J/S2020第二轮提高组北京市获奖分析 CCF CSP-J/S2020第二轮评级规则 2020CSP-J成绩 【ZZ】各省市大学升学率 [ZZ]80% 的信息学竞赛生都不知道的 USACO!含金量极高 初步估分体验 【ZZ】近3年北京市科技特长生招生趋势分析,编程好的孩子有哪些优势? 立冬--2020CSP-J 游记 信息学奥赛(NOIP)经典书目汇总! 【参赛试题】海淀区第三届智慧杯编程思维类复赛(C++)题目&题解&标程 【ZZ】从入门到高阶,你需要刷哪些书?丨高中物理竞赛辅导书推荐 小丸子的记录 【zz】欧几里德与扩展欧几里德算法相关 【ZZ】kali下搭建WiFi钓鱼热点 【ZZ】二分查找七种情况大合集 油腻中年大叔回来了 值得纪念的2007
【ZZ】二分算法总结
魔古力 · 2019-08-18 · via 博客园 - 魔古力

俗话说的好啊,十个二分九个错.......
今天在写那个最长递增子序列的O(nlgn)算法的时候
着实是被这个二分算法恶心到了.......
看似那么简单的一个算法
但是终止条件,边界条件,溢出........
各种坑.......
等我把手头事情写完,我一定来好好总结一下这个一共有64种写法的二分算法.........吐.............


二分算法总结

二分算法的写法实在是太多了,又容易出错
其实对于我们来说,它有多少种写法并不重要
重要的是!!你能不能bug free的按要求写出其中一种来!
今天就来总结一下经常用到的几种二分写法。
---
首先,区间问题
区间常用的有两种
一种[low, high),另一种[low, high]
为了方便记忆,不易出错,就老老实实地用第二种吧

中间值问题
确定了用第二种左闭右闭区间
则中间值就是:

mid = low + (high - low) / 2;

为什么不用下面这个呢?

mid  = (low + high) / 2;

是因为low+high有溢出的风险,加起来可能超过INT_MAX

终止条件
终止条件只有一个就是

low > high

也就是搜索空间为空

返回值
确定了终止条件,返回值就很简单了,直接返回low值就行了。


好了,现在就来写一下最经典的,没有重复值,返回插入位置的写法
这都是经过leetcode检验的

int binary_search(int arr[], int low, int high, int val)
{
    int mid;
    while (low <= high)
    {
        mid = low + (high - low) / 2;
        if (arr[mid] < val)    low = mid + 1;
        else if (arr[mid] > val)   high = mid - 1;
        else   return mid;
    }
    return low;
}

如果有重复值,要返回第一个出现的位置,怎么办呢?
其实有重复值的话,其实也简单,只需要改一点点就行了
其实这个就对应C++STL里面的lower_bound()函数

int binary_search(int arr[], int low, int high, int val)
{
    int mid;
    while (low <= high)
    {
        mid = low + (high - low) / 2;
        if (arr[mid] < val)   low = mid + 1;
        else    high = mid - 1;       

那如果有重复值,要求返回最后一个应该插入的位置,怎么办呢
也就是返回第一个大于val的元素的位置
其实这就对应了C++STL里面的upper_bound()函数
将上面的稍微改一下就行了

int binary_search(int arr[], int low, int high, int val)
{
    int mid;
    while (low <= high)
    {
        mid = low + (high - low) / 2;
        if (arr[mid] > val)    high = mid - 1;
        else   low = mid + 1;   

总结
其实要注意的地方都有固定的写法,只要按照这种写法写,就一定不会出错
不然每次都要考虑到底是low < high 还是 low <= high?到底什么时候low = mid + 1, 什么时候 high = mid -1,。。。简直要疯掉。。。。

所以,只需要记住固定的用法就好了

注意的点就只有几个:
第一:永远都用左闭右闭区间,如果题目是左闭右开,也转成左闭右闭
第二:永远用 low <= high 的循环条件
第三:永远返回 low
第四:如果是没有重复值,那么直接最基本的判断就行了
第五:难点就是lowwer_bound和upper_bound的时候,技巧心法就是lowwer_bound是要返回重复值的第一个,那么如果当arr[mid]在一群val中间时,是应该取左边的部分,即high应该往左边靠,也即<和=应该合并在一起;同理,upper_bound是要返回右边的部分,所以相等的情况应该和low往右边靠的过程合并在一起,即>和=应该合并在一起

只要记住这几点,二分法绝对不会再出错!~