【ZZ】二分算法总结

俗话说的好啊,十个二分九个错.......
今天在写那个最长递增子序列的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;       // arr[mid] <= val,等于时也把high降低,
                                    //因为最终low会越过high的   
    }
    return low;
}

那如果有重复值,要求返回最后一个应该插入的位置,怎么办呢
也就是返回第一个大于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;   // 因为找第一个大于val的值,所有显然当等于时也应该提高low
    }
    return low;
}

总结
其实要注意的地方都有固定的写法,只要按照这种写法写,就一定不会出错
不然每次都要考虑到底是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往右边靠的过程合并在一起,即>和=应该合并在一起

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

posted @ 2019-08-18 16:08  魔古力  阅读(394)  评论(0编辑  收藏  举报