二分查找

二分查找

  • 核心:每次缩小猜测范围到上次的一半
  • 前提:有序

二分查找函数

  • 写一个函数BinarySearch,在包含size个元素的、从小到大排序的int数组a里查找元素p。如果找到,则返回元素下标;如果找不到,则返回-1。要求复杂度O(log(n))。
int BinarySearch(int a[], int size, int p)
{
	int L = 0;	//查找区间的左端点 
	int R = size - 1;	//查找区间的右端点 
	while(L <= R){	//如果查找区间不为空就继续查找 
		int mid = L + (R-L)/2;	//取查找区间正中元素的下标 
		if(p == a[mid])
			return mid;
		else if(p > a[mid])
			L = mid +1;	//设置新的查找区间的左端点 
		else
			R = mid -1;	//设置新的查找区间的右端点 
	}
	return -1;
}	//复杂度O(log(n)) 
  • 写一个函数LowerBound,在包含size个元素的、从小到大排序的int数组a里查找比给定整数p小的、下标最大的元素。找到则返回其下标,找不到则返回-1。
int LowerBound(int a[], int size, int p)
{
	int L = 0;	//左 
	int R = size - 1;	//右 
	int lastPos = -1;	//到目前为止找到的最优解 
	while(L <= R){	//如果查找区间不为空则继续查找 
		int mid = L+(R-L)/2;	//取查找区间正中元素的下标 
		if(a[mid >= p])
			R = mid -1;
		else{
			lastPos = mid;
			L = mid+1;
		}
	}
	return lastPos;
 }	//复杂度O(log(n)) 
  • 注意:
    取查找区间正中元素的下标时:int mid = (L+R)/2;
    (L+R)可能过大溢出int所能表示的范围,故:int mid = L+(R-L)/2;

例题

二分法求方程的根

求下面一个方程的根:f(x) = x³-5x²+10x-80=0
若求出的根是a,则要求 |f(a)| <= 10^-6

  • 解法:f(x) 单调递增,f(0)<0, f(100)>0.可用二分法在区间 [0,100] 中寻找根。
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<iomanip>
using namespace std;
double EPS = 1e-6;
double f(double x)	{ return x*x*x-5*x*x+10*x-80; }
int main(){
	double root, x1 = 0, x2 = 100, y;
	root = x1 + (x2-x1)/2;
	int triedTimes = 1;	//记录一共尝试多少次,对于求根来说不是必须的
	y = f(root);
	while( fabs(y) > EPS){
		if(y > 0)	x2 = root;
		else		x1 = root;
		root = x1+(x2-x1)/2;
		y = f(root);
		triedTimes ++;
	}
	cout<<fixed<<setprecision(8)<<root<<endl;
	cout<<triedTimes;
	return 0;
}
寻找指定和的整数对

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

  • 解法1:用两重循环,枚举所有的取数方法,复杂度是O(n²)
for(int i = 0; i < n-1; ++i)
	for(int j = i + 1; j < n; ++j)
		if(a[i]+a[j] == m)
			break;
	100,000²=100亿,超时!
  • 解法2:
    将数组排序,复杂度是O(n*log(n))

    对数组中的每个元素 a[i],在数组中二分查找m-a[i],看能否找到。复杂度log(n),最坏要查找n-2次,所以查找这部分的复杂度也是O(n*log(n))

    这种解法总的复杂度是O(n*log(n))

  • 解法3:
    将数组排序,复杂度是O(n*log(n))

    查找的时候,设置两个变量 i 和 j,i 初值是0,j 初值是n-1。看 a[i]+a[j],如果大于m,就让 j 减1,如果小于m,就让 i 加1,直至 a[i]+a[j]=m。

    这种解法的复杂度是O(n*log(n))

百练2456:Aggressive cows

百练2456
农夫John建造了一座很长的畜栏,它包括N(2<=N<=100,000)个隔间,这些小隔间的位置为x0,…,xn-1(0<=xi<=1,000,000,000,均为整数,各不相同)。
John的C(2<=C<=N)头牛每头分到一个隔间。牛都希望互相离得远点。怎样才能使任意两头牛之间的最小距离尽可能大。这个最大的最小距离是多少呢?

  • 解法1:穷举
    复杂度1,000,000,000/C*N,即1,000,000,000,超时

  • 解法2:二分法
    先得到排序后的隔间坐标x0,…,xn-1
    在 [L,R] 内用二分法尝试“最大最近距离” D=(L+R)/2(L,R初值为[1,1,000,000,000/C])
    若D可行,则记住该D,然后在新 [L,R] 中继续尝试(L=D+1)
    若D不可行,则在新 [L,R] 中继续尝试(R=D-1)
    复杂度log(1,000,000,000/C)*N

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值