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往右边靠的过程合并在一起,即>和=应该合并在一起

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

  • 相关阅读:
    51 Nod 1086 多重背包问题(单调队列优化)
    51 Nod 1086 多重背包问题(二进制优化)
    51 Nod 1085 01背包问题
    poj 2559 Largest Rectangle(单调栈)
    51 Nod 1089 最长回文子串(Manacher算法)
    51 Nod N的阶乘的长度 (斯特林近似)
    51 Nod 1134 最长递增子序列(经典问题回顾)
    51 Nod 1020 逆序排列
    PCA-主成分分析(Principal components analysis)
    Python中cPickle
  • 原文地址:https://www.cnblogs.com/yyehl/p/7117272.html
Copyright © 2011-2022 走看看