二分查找 | C++
以此题为例:P2249 【深基13.例1】查找
二分查找
对于一个单调不降的序列 \(S\),传统查找的复杂度是 \(O(|S|)\),即 \(O(n)\). 有时候序列 \(S\) 中的元素特别多,或者你希望尽量减小复杂度,那么,有没有复杂度更低的方法呢? 理论上是不行的,因为读入的复杂度已经达到O(n),而且大多数时候我们还需要 O(n·logn) 的复杂度对序列排序
然而实际上(比如例题),有时候我们需要查找很多次,读入时复杂度是 \(O(n + m)\),然而查找时复杂度就成了 \(O(nm)\),这时就有可能超时。是否有其他的算法可以降低复杂度呢?
当然是有的。我们可以使用 STL 里的 lower_bound 或 upper_bound. 还记得你是怎么在英语词典里查单词的吗?字典中的单词是按照“字典序”进行排序的(类似例题中的单调不降)。如果我们要找一个单词,就要将字典从中间翻开,然后将这面单词跟想要找的单词比较。如果这面单词在需要寻找的单词之前,就将字典往后翻,否则就往前翻,直到找到准确的单词为止。(这句话与推广部分第一句皆引用自洛谷)
我们可以使用类似的方式进行查找,即二分查找。二分查找时,先判断序列 \(S\) 最中间的元素 \(S_{\frac{|S|}{2}}\) 与查找的元素之间的大小关系,由于序列是单调不减的,因此我们可以依据刚刚判断得到的关系进一步对序列 \(S\) 一半的区间继续使用相同的方式查询,直到得出结果为止
二分查找的实现
只需要按我刚才讲解的进行模拟即可,注意该序列可能存在重复元素,如果待查找元素在该序列存在多个,千万不要使用 while 循环往前一个一个推,我们继续二分即可,否则复杂度会退化,极端情况可能退化至 \(O(\frac {n \cdot m} {2})\) 仍然可能超时。
继续二分的策略的缺点是将复杂度锁死在 \(O(m \cdot logn)\),但这个方式已经将算法的时间复杂度拉得够低了,足够过例题了。具体代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int a[N];
int n, m, x;
int find() {
int mid, l = 1, r = n, ans = -1;
scanf("%d", &x);
while(l <= r) {
mid = (l + r) / 2;
if (x < a[mid]) r = mid - 1;
else if (x > a[mid]) l = mid + 1;
else {
r = mid - 1;
ans = mid;
}
}
return ans;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= m; i++) printf("%d ", find());
return 0;
}
使用递归实现二分
等一等,不停使用相同方式查询并缩小范围,有没有感觉很熟悉?没错,二分查找还可以使用递归实现,代码更加优美,更适合装B.
具体代码如下:
#include <bits/stdc++.h>
const int N = 1e6 + 5;
int st[N];
int n, m;
int find(int ans, int num, int* arr, int l, int r) {
if (l > r) return ans; int mid = l + (r - l) / 2;
if (arr[mid] < num) return find(ans, num, arr, mid + 1, r);
if (arr[mid] > num) return find(ans, num, arr, l, mid - 1);
ans = mid; return find(ans, num, arr, l, mid - 1);
return ans;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &st[i]);
for (int i = 1, tmp; i <= m; i++) {
scanf("%d", &tmp);
printf("%d ", find(-1, tmp, st, 1, n));
}
return 0;
}
二分思想的推广
除了二分查找之外,二分思想还能求出可行解的最值问题,比如想知道某款手机最高能多少楼高度摔下来而不会摔坏,使用二分的方式可以用最小实验次数就能得到结果(当然你需要准备好几个样品),这种思想被称为二分答案。二分思想本身非常简单,大家只需要多练习,很快就可以掌握二分查找和二分答案。不说了,赶紧去刷题!
二分查找 | C++的更多相关文章
- jvascript 顺序查找和二分查找法
第一种:顺序查找法 中心思想:和数组中的值逐个比对! /* * 参数说明: * array:传入数组 * findVal:传入需要查找的数 */ function Orderseach(array,f ...
- Java实现的二分查找算法
二分查找又称折半查找,它是一种效率较高的查找方法. 折半查找的算法思想是将数列按有序化(递增或递减)排列,查找过程中采用跳跃式方式查找,即先以有序数列的中点位置为比较对象,如果要找的元素值小 于该中点 ...
- 从一个NOI题目再学习二分查找。
二分法的基本思路是对一个有序序列(递增递减都可以)查找时,测试一个中间下标处的值,若值比期待值小,则在更大的一侧进行查找(反之亦然),查找时再次二分.这比顺序访问要少很多访问量,效率很高. 设:low ...
- java实现二分查找
/** * 二分查找 * @param a * @param n * @param value * @return * @date 2016-10-8 * @author shaobn */ publ ...
- 最新IP地址数据库 二分逼近&二分查找 高效解析800万大数据之区域分布
最新IP地址数据库 来自 qqzeng.com 利用二分逼近法(bisection method) ,每秒300多万, 比较高效! 原来的顺序查找算法 效率比较低 readonly string i ...
- c#-二分查找-算法
折半搜索,也称二分查找算法.二分搜索,是一种在有序数组中查找某一特定元素的搜索算法. A 搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束: B 如果某一特定元素大于或者小 ...
- 【Python】二分查找算法
二分查找:在一段数字内,找到中间值,判断要找的值和中间值大小的比较.如果中间值大一些,则在中间值的左侧区域继续按照上述方式查找.如果中间值小一些,则在中间值的右侧区域继续按照上述方式查找.直到找到我们 ...
- PHP实现文本快速查找 - 二分查找
PHP实现文本快速查找 - 二分查找法 起因 先说说事情的起因,最近在分析数据时经常遇到一种场景,代码需要频繁的读某一张数据库的表,比如根据地区ID获取地区名称.根据网站分类ID获取分类名称.根据关键 ...
- java二分查找举例讨论
最近做笔试题有这么一个关于二分查找的例子. 给一个有序数组,和一个查找目标,用二分查找找出目标所在index,如果不存在,则返回-1-(其应该出现的位置),比如在0,6,9,15,18中找15,返回3 ...
- JAVA源码走读(二)二分查找与Arrays类
给数组赋值:通过fill方法. 对数组排序:通过sort方法,按升序.比较数组:通过equals方法比较数组中元素值是否相等.查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找 ...
随机推荐
- 一款开源的.NET程序集反编译、编辑和调试神器
前言 说到.NET相关的反编译工具大家脑海里第一个想到的工具是什么?ILSpy.dnSpy.还是dotPeek?咱们今天的主要内容是讲讲dnSpyEx(dnSpyEx是dnSpy项目的非官方Fork维 ...
- centos7系统的七个运行级别和设置默认运行级别
一.系统七个运行级别概述 0 系统停机模式,系统默认运行级别不能设置为0,否则不能正常启动,机器关的 1 单用户模式,root权限,用于系统维护,禁止远程登陆,就像Windows下的安全模式登录 2 ...
- 为什么wait()、notify()方法需要和synchronized一起使用
提示:更多优秀博文请移步博主的GitHub仓库:GitHub学习笔记.Gitee学习笔记 Obj.wait()与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wai ...
- 2024盘古石取证比赛(APK)
题目列表 使用软件: Notepad++,火眼证据分析软件,雷电分析app,DB browser for SQLCipher 1. 分析伏季雅的手机检材,手机中诈骗APP的包名是:[答案格式:abc. ...
- 挨个配置资源组太麻烦?ROS伪参数一步搞定!
介绍 伪参数 伪参数是资源编排服务ROS的编排引擎提供的固定参数,即在编写模板时可以使用的一系列预定义的参数,它们为模板提供了资源部署过程中的环境和执行上下文信息. 更多伪参数介绍请查看:ROS伪参数 ...
- PaddleOCR在 Linux下的webAPI部署方案
很多小伙伴在使用OCR时都希望能过采用API的方式调用,这样就可以跨端跨平台了.本文将介绍一种基于python的PaddleOCR识方案.喜欢的可以关注公众号,获取更多内容. 一. Linux环境下部 ...
- js 表格的添加和删除操作
大江东去,浪淘尽,千古风流人物.故垒西边,人道是,三国周郎赤壁.乱石穿空,惊涛拍岸,卷起千堆雪.江山如画,一时多少豪杰.遥想公瑾当年,小乔初嫁了,雄姿英发.羽扇纶巾,谈笑间,樯橹灰飞烟灭.故国 ...
- ajax 请求接口实例
通过 HTTP 请求加载远程数据. jQuery 底层 AJAX 实现.简单易用的高层实现见 $.get, $.post 等.$.ajax() 返回其创建的 XMLHttpRequest 对象.大多数 ...
- Redis 常用的数据结构简介与实例测试【Redis 系列二】
〇.都有哪些数据结构? Redis 提供了较为丰富的数据类型,常见的有五种:String(字符串),Hash(哈希),List(列表),Set(集合).Zset(有序集合). 随着 Redis 版本的 ...
- react移动端组件antd-mobile
使用react移动端组件antd-mobile完成底部导航功能实现. 官网:https://mobile.ant.design/docs/react/introduce-cn antd-mobile ...