Day3二分查找(跳过快速排序,归并排序)

二分,二分查找

用于有序,有单调性的问题
有单调性的问题一定可以用二分法解决,反过来,用二分法能解决的问题不一定有单调性。

整数二分——两种模型

一、两种模型基本原理

两种模型,分别用来查找满足条件的左右界。
l:查找的数据左边界
r:查找的数据右边界
A:满足条件的右界
B:满足条件的左界
在这里插入图片描述
模型一:
在这里插入图片描述
模型二:

在这里插入图片描述

二、模型记忆点

1.模型概括
1)左边满足,求满足右界:
mid=l+r +1/2 ; 满足 l=mid ; 不满足 r=mid -1
2)右边 满足,求满足 左界
mid=l+r/2 ; 满足 r=mid ; 不满足 l=mid +1

2.记忆点

  • 记忆点一:
    左边 满足,满足时更新数据 左边界 等于mid
    右边 满足,满足时更新数据 右边界 等于mid
  • 记忆点二:
    左边满足时,mid取半要 加1,同时不满足的更新对应 减1
    右边满足时,mid取半 不加1,同时不满足的更新对应 加1
三、模型细节问题思考
  • 问题一:为什么mid一个取l+r+1/2,一个取l+r:
    int除法只能得int而且是上取整,即2+1/2结果为1,而不是1.5,不是2。
    模板一,若取mid=l+r/2,到最后剩2个数时出现问题,如下图剩余两个m,n

在这里插入图片描述
mid=l+r的话,mid指向第一个数,如果m满足条件,l=mid,然而一直l=mid,相当于一直不更新,死循环。

  • 问题二:更新(或说是循环)的结果是l,r相遇。
    l<r是循环条件,while(l<r)
    循环最终结局l=r是唯一可能性吗,r可能小于l吗?
    如果不可能,应该也可以下边这么写
while(1){
	...
	if(l=r) break;
}
四、模型的经典代码模板

yxc给出的代码模板

模型一:

int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    return l;
}

模型二:

int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

学习到的
l+r>>1中>>表示位运算的“右移”运算,等同于除2,也是向下取整。
如:

int a=11,b;  //a二进制为0000 1011
b=a>>2;  //b为0000 0101,即为5
五、练习题目

https://www.acwing.com/problem/content/description/791/

扫描二维码关注公众号,回复: 10449461 查看本文章

我自己用while1, l=r时break写的,结果有错没心情再看了,不知道哪里错:

#include <iostream>
using namespace std;
int find_l(int a[],int x,int l,int r);
int find_r(int a[],int x,int l,int r);
int find_l(int a[],int x,int l,int r){
	while(1){
		int mid;
		mid=l+r>>1;
		if(a[mid]>=x)	r=mid;
		else  l=mid+1;
		if(l==r) break;
	} 
	if(a[l]==x)	return l+1;//最终得出的l是下标,但是求的是第几位所以加1 
	else return -1;	
	}
int find_r(int a[],int x,int l,int r){
	while(1){
		int mid;
		mid=l+r+1>>1;
		if(a[mid]<=x) l=mid;
		else r=mid-1;
		if(l==r) break;
	}
	if(a[l]==x)	return l+1;//最终得出的l是下标,但是求的是第几位所以加1 
	else return -1;	
}
int main(){
	int n,a[10],b[10],q;
	cin>>n>>q;
	for(int i=0;i<=n-1;i++)	cin>>a[i];
	for(int i=0;i<=q-1;i++)	cin>>b[i];
	for(int i=0;i<=q-1;i++){
		cout<<find_l(a,b[i],0,n-1)<<" "<<find_r(a,b[i],0,n-1)<<"\n";
	}
	return 0;
}

浮点二分

一、基本原理

1.原理类似。
因为不涉及上下取整问题,所以没有取中点需要加加一的问题。
浮点数是线段式的连续,不是整型数的一个一个点,所以更新边界不会加一减一。
2.关于循环条件:因为不断取中点,有可能最终无法达到l=r,有可能l一直小于r,因此循环条件是l<r有可能无法跳出,所以循环条件可以改成l-r的值足够小时,如10^-6。

二、练习题目

求三次方根:https://www.acwing.com/problem/content/792/

#include <iostream>
#include <math.h>
using namespace std;
double f(double a){
	double l=0,r,mid;
	r=fabs(a);
	while(r-l>1e-10){
		mid=(l+r)/2.0;
		if(mid*mid*mid<=fabs(a)) l=mid;
		else r=mid;
		}	
	if(a>=0) return l;
	else return -l;
}
int main(){
	double a;
	cin>>a;
	printf("%f\n",f(a));
	return 0;
}

学习到的
1.10的几次方怎么写:10的-6次方,1e-6
2.保留5位小数,最后输出时用cout一直报错,改成printf
3.math.h数学库:
取绝对值 fabs(x) 、x的三次方 pow(x)

发布了20 篇原创文章 · 获赞 0 · 访问量 714

猜你喜欢

转载自blog.csdn.net/m0_37733257/article/details/104417679
今日推荐