二分法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分法相关的知识,希望对你有一定的参考价值。
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 //
//*******************************************************//
以上是关于二分法的主要内容,如果未能解决你的问题,请参考以下文章