算法基础学习笔记-4-二分算法

4.二分算法

4.1 二分查找函数
  • 写一个函数BinarySeach,在包含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; //取查找区间正中元素的下标,防止(L+R)过大溢出
		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) //复杂度O(log(n))
{
	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;
}
4.2 二分法求方程的根

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

解题思路:对f(x)求导,得f’(x)=3x2-10x+10。由一元二次方程求根公式知方程f’(x)= 0 无解,因此f’(x)恒大于0。故f(x)是单调递增的。易知 f(0) < 0且f(100)>0,所以区间[0,100]内必然有且只有一个根。由于f(x)在[0,100]内是单调的,所以可以用二分的办法在区间[0,100]中寻找根。

#include <cstdio>
#include <iostream>
#include <cmath>
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++;
	}
	printf("%.8f\n", root);
	printf("%d", triedTimes);
	return 0;
}
4.3 寻找指定和的整数对

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

  • 解法一

    用两重循环,枚举所有的取数方法,复杂度是O(n2)的。

  • 解法二

    1. 将数组排序,复杂度是O(n×log(n))

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

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

  • 解法三

    1. 将数组排序,复杂度是O(n×log(n))

    2. 查找的时候,设置两个变量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))的。

4.4 农夫和奶牛

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

  • 解法1
    先得到排序后的隔间坐标 x0,…,xN-1
    从1,000,000,000/C到1依次尝试这个“最大的最近距离”D, 找到的第一个可行的就是答案。
    尝试方法:
    1. 第1头牛放在x0
    2. 若第k头牛放在xi ,则找到xi+1到xN-1中第一个位于[xi+D, 1,000,000,000]中的Xj ,第k+1头牛放在Xj。找不到这样的Xj,则 D=D-1,转 1)再试
      若所有牛都能放下,则D即答案
      复杂度 *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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值