C++二分查找

二分查找简介

我们有一个有序序列,这个序列分为前后两个部分,分界点往前都不符合要求,往后都符合要求或者往前都符合要求,往后都不符合要求时,且我们需要在其中找到第一个满足要求或者第一个不满足要求或者最后一个满足要求或者最后一个不满足要求的元素位置时,我们往往会用二分查找来解决。

二分查找,顾名思义,就是每次取左端点和右端点的平均值,判断这个平均值是否符合要求,之后按情况改变左端点或右端点的值,直到左端点大于右端点输出答案(过会儿会详细讲解)。

那为什么不从头到尾枚举一遍呢?如果从头到尾枚举一遍,要是刚开始就有一个元素符合要求,那还好办,万一到最后才有符合要求的可就是On级别的时间复杂度了,可如果用二分查找就只用log2(n)级别了,大大减少了时间复杂度。

举个简单的例子:

题目描述:
给出一段序列和一个k,找出序列中第一个大于等于k的元素的位置。
保证序列为单调递增的序列。

输入格式:
第一行:两个正整数N和k,表示了序列的长度和要比较的值。
第二行:包含N个整数num[i],描述了这段序列。

输出格式:
一个整数,为第一个符合要求的元素的位置是多少。
如果没有符合要求的数

输入样例:

5 10
1 8 10 10 15

输出样例:

3

解决方法

先定义左端点l = 1,右端点r = n。之后开始循环直到左端点大于右端点才停止。循环中每次,取中点(即(l + r) / 2),判断中点是否符合要求,在此题中分界点前为不符合要求的,往后为符合要求的,是形如×××××××√√√√√√的样式,所以如果说中点符合要求(既为√),就让r = mid - 1,并且更新ans的值为mid。否则让l = mid + 1。

想清楚了思路,就可以开始写代码了。

推荐建立一个check函数,这样当要求变得更多时,更有利于理清逻辑。

(PS:NR是指序列长度的上限)

# include <cstdio>
# include <iostream>
# include <cmath>
# include <cstring>
# include <algorithm>
using namespace std;

# define FOR(i, a, b) for(int i = a; i <= b; i++)
# define _FOR(i, a, b) for(int i = a; i >= b; i--)

const int NR = 100000;

int n, k;
int a[NR + 10];

bool check(int x){
	return a[x] >= k;
}

int main()
{
	scanf("%d%d", &n, &k);
	FOR(i, 1, n) scanf("%d", &a[i]);
	int l = 1, r = n, ans = -1;
	while(l <= r){
		int mid = (l + r) / 2;
		if(check(mid)) r = mid - 1, ans = mid;
		else l = mid + 1;
	}
	printf("%d", ans);
	return 0;
}

:如果你不想每次都怎么麻烦去写长长的模版,那就学习一下几个有用的二分函数吧,此网站有二分函数的教学。
https://blog.csdn.net/SkeletonKing233/article/details/99479707

God Bless You For Ever!

发布了33 篇原创文章 · 获赞 47 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览