zoukankan      html  css  js  c++  java
  • 二分法

    2017-08-06    22:48:56

    一、定义

      二分查找 又称为折半查找 , 是一种查找效率较高的方法 。

      要求 : 1 . 所查找的序列为有序序列

           2. 只能在顺序存储结构上实现

    二、基本思想

      每次将给定的 key 值与有序表中间位置上记录的数据进行比较 ,确定待查记录所在的范围 , 然后逐渐缩小查找范围 , 直到确定找到或找不到 。

    三、查找过程

      每次执行的操作是将  key  依次与  mid  对应的数据进行比较 。

     

    注意 :

      mid = (  low + high ) / 2  , 可能会因为 low + high 超范围溢出 , 所以在写的时候 mid = low + ( high - low ) /  2 ;

    举一个例题 :

      输入n ( n<= 100,000) 个整数, 找出其中的两个数,它们之和等于 整数m( 假定肯定有解) 。 题中所有整数都能用 int 。

      方法一 : 二分找数 。

      方法二 :1 . 对数组进行排序 , 复杂度为 n * log n  ; 

           2 . 设置两个下标 i 和 j , 另 i = 0 , j = n - 1  , 判断 a[i] + a[j] == m  , 若 a[i] + a[j] > m , j--  , 否则如果 a[i] + a[j] < m , i++ 。 这样下来复杂度为  n  。

          综合起来复杂度为  n * log n 。

    代码示例 :

      非递归版本

      

    /*******************************/
    // 非递归版的二分法 
    void search ( int low , int high , int key ) {
    	
    	while ( low <= high ) {
    		int mid = ( low + high ) / 2 ;
    		if ( key == a[mid] ) {
    			cout << "找到" ;
    			return ;  // return 的作用是为了跳出函数,并且返回值为空类型 
    		}  
    		if ( key > a[mid] ) low = mid + 1 ;
    		else high = mid - 1 ;
    	}
    	cout << "未找到" ;  // 若前面所要找的数未找到,则函数不会返回,此时会执行到这一步。 
    } 
    

    递归版本

     

    /*******************************/
    // 递归版的二分法 
    void search ( int low , int high , int key ) {
    	int mid = ( low + high ) / 2 ;
    	
    	if ( low >= high ) {
    		cout << "未找到" ;
    		return ; 
    	}
    	if ( a[mid] == key ) {
    		cout << "找到" ;
    		return ;
    	}
    	if ( a[mid] > key ) search ( low , mid - 1 , key ) ;
    	else search ( mid + 1 , high , key ) ;
    }
    

     

    时间复杂度:

      由于二分法每次是折半分 , 设复杂度为 k , 二分的所有数据总数为 n , 则 2^k = n  , 复杂度 k = lg n 。

    二分查找 : 

      在做二分题的时候会遇到一类二分查找的题 , 此类题的特点是 直接找到要二分的首区间与尾区间 , 一直二分找最优解 ,直到退出循环条件 。

      方法一 :

    int fun ( int key , int l , int r ) {
    	int mid ;
    	while ( l <= r ) {
    		mid = l + ( r - l ) / 2 ;
    		if ( a[mid] == key ) return mid ;
    		if ( a[mid] < key ) l = mid + 1 ;
    		else r = mid - 1 ;		
    	}
    	return -1 ;
    }

    // 在不提前返回的前提下 , 二分的退出条件是 l > r  , 退出循环的前一次是 l = r = mid  

      方法二:

    int fun ( int key , int l , int r ) {
    	int mid ;
    	
    	while ( l < r ) {   // 在二分的过程中不返回值的情况 
    		mid = l + ( r - l ) / 2 ;
    		if ( a[mid] < key ) l = mid + 1 ;
    		else r = mid ;
    	}
    	if ( a[mid] == key ) return mid ;
    	else return -1 ;
    }
    /// 这种方式写的二分 , 循环最终退出是因为 l 一直向左移 , 最终等于 r , 此时 l 等于 r ,结束循环 
    /// 而此时 r 也记录着最后一次 mid 值  
    

      

    有一种情况也很特殊 :

    int fun ( int key , int l , int r ) {
    	int mid ;
    	while ( l <= r ) {
    		mid = l + ( r - l ) / 2 ;
    		if ( a[mid] == key ) return mid ;
    		if ( a[mid] < key ) l = mid;   //若二分这样写 , 则可能造成循环退不出去  
    		else r = mid ;		// 同上 , 但 有种情况可以这样写 ,就是在求一个方程的零点时
    							// 因为零点不会是整数点 ,此时这样写不会有问题 ,但为了确保
    							// 肯定没问题 , 循环可以不用 while 控制 , 直接二分100次 
    	}
    	return -1 ;
    }
    

      

    例题 :

    问题描述
      有N个城市,每个城市有Ai个人。
      现在要开始投票,每个人有一张票。
      作为领导者,你有B个箱子,你必须要将这B个箱子分发到N个城市去,每个城市至少需要一个箱子。
      每个人都必须要投票,不能弃票,也就是说要把票丢进箱子里去(每个城市有Ai张票)。
      现在问你,怎么分配这B个箱子才能使这些所有箱子里中票数最大的那个箱子里的票最少?

    输入
      输入包含一个数N(1<=N<=500,000)和B(N<=B<=2,000,000)。
      接下来N行包含N个数,每个数Ai(1<=Ai<=5,000,000)表示每个城市的人数。
      输入N为-1,B为-1时结束。

    输出
      输出那个带兵量最多的将领所带的士兵数。

    样例输入
      2 7
      200000
      500000
      4 6
      120
      2680
      3400
      200
      -1 -1

    样例输出
      100000
      1700

    /**************************************/

    补充一道 HDU 2141   http://acm.hdu.edu.cn/showproblem.php?pid=2141         // 

    //*******************************************************//

      

       

    东北日出西边雨 道是无情却有情
  • 相关阅读:
    使用System.Web.Mail通过需验证的邮件服务器发送邮件(转) dodo
    报错显示"从客户端检测到有潜在危险的Request.Form 值" dodo
    setting页面出现的原理 dodo
    action的原理 dodo
    dnn一些公用方法分析 dodo
    缓存依赖概述 dodo
    在Intranet环境中保护.NETWeb应用程序方案(转) dodo
    dnn中的用户密码设置 dodo
    Application_Start事件中修改配置文件内容 dodo
    “System.Data.OleDb.OleDbException: 未指定的错误” dodo
  • 原文地址:https://www.cnblogs.com/ccut-ry/p/7294456.html
Copyright © 2011-2022 走看看