zoukankan      html  css  js  c++  java
  • 二分算法来相会

    俗话说的好啊,十个二分九个错.......
    今天在写那个最长递增子序列的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往右边靠的过程合并在一起,即>和=应该合并在一起

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

  • 相关阅读:
    Run Loop简介 分类: ios技术 ios相关 2015-03-11 22:21 73人阅读 评论(0) 收藏
    iOS Socket第三方开源类库 ----AsyncSocket 分类: ios相关 ios技术 2015-03-11 22:14 59人阅读 评论(0) 收藏
    CocoaPods安装和使用教程 分类: ios技术 ios相关 2015-03-11 21:53 48人阅读 评论(0) 收藏
    详解Objective-C的meta-class 分类: ios相关 ios技术 2015-03-07 15:41 51人阅读 评论(0) 收藏
    IOS即时通讯XMPP搭建openfire服务器 分类: ios技术 2015-03-07 11:30 53人阅读 评论(0) 收藏
    IOS之富文本编辑 分类: ios技术 2015-03-06 22:51 89人阅读 评论(0) 收藏
    IOS第三方数据库--FMDB 分类: ios技术 2015-03-01 09:38 57人阅读 评论(0) 收藏
    ASIHTTPRequest异步请求 分类: ios技术 2015-03-01 09:33 48人阅读 评论(0) 收藏
    python+day6 json,pickle 模块
    python_day6 log模块
  • 原文地址:https://www.cnblogs.com/yyehl/p/7117272.html
Copyright © 2011-2022 走看看