【函数题】6-10 二分查找
6-10 二分查找
- 1 题目原文
- 2 思路解析
- 2.1 基本二分查找算法
- 2.2 常用二分模板
- 2.2.1 第一个大于等于目标值的元素下标
- 2.2.2 第一个大于目标值的元素下标
- 2.2.3 最后一个小于等于目标值的元素下标
- 2.2.3 最后一个小于目标值的元素下标
- 2.2.4 小结
- 3 代码实现
- 3.1 本题代码实现
- 3.1.1 递归法
- 3.1.2 迭代法
- 3.2 常用模板
- 2.2.1 第一个大于等于目标值的元素下标
- 2.2.2 第一个大于目标值的元素下标
- 4 总结
1 题目原文
题目链接:6-10 二分查找
本题要求实现二分查找算法。
函数接口定义:
Position BinarySearch( List L, ElementType X );
其中 List 结构定义如下:
typedef int Position;
typedef struct LNode *List;
struct LNode {ElementType Data[MAXSIZE];Position Last; /* 保存线性表中最后一个元素的位置 */
};
L 是用户传入的一个线性表,其中 ElementType 元素可以通过 >、==、< 进行比较,并且题目保证传入的数据是递增有序的。函数 BinarySearch 要查找 X 在 Data 中的位置,即数组下标(注意:元素从下标 1 开始存储)。找到则返回下标,否则返回一个特殊的失败标记 NotFound。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>#define MAXSIZE 10
#define NotFound 0
typedef int ElementType;typedef int Position;
typedef struct LNode *List;
struct LNode {ElementType Data[MAXSIZE];Position Last; /* 保存线性表中最后一个元素的位置 */
};List ReadInput(); /* 裁判实现,细节不表。元素从下标1开始存储 */
Position BinarySearch( List L, ElementType X );int main()
{List L;ElementType X;Position P;L = ReadInput();scanf("%d", &X);P = BinarySearch( L, X );printf("%d\n", P);return 0;
}/* 你的代码将被嵌在这里 */
输入样例1:
5
12 31 55 89 101
31
输出样例1:
2
输入样例2:
3
26 78 233
31
输出样例2:
0
代码长度限制 16 KB
时间限制 100 ms
内存限制 64 MB
2 思路解析
在本篇文章中,除了解决题目以外,还将简单地总结一下二分查找算法,二分查找是一个灵活的算法,即使有基本模板可以套用,但是一般的二分查找题目都需要对基本模板进行修改,这需要读者能深刻理解二分查找算法的原理(原理并不难)。而且基本模板的实现细节也因人而异,选择自己喜欢的一个基本模板即可,只需要记住一套基本模板,然后随题意去修改这个基本模板即可。
2.1 基本二分查找算法
基本二分查找算法就是从一个升序数组 arr 中查找元素 X 是否存在,注意数组中的数是不重复的。目的一般是返回元素 X 在数组中的下标,如果不存在则返回一个约定的值(常见的是 -1,数组长度 n等)。
我现在不知道这个数在哪里,甚至连它在不在数组里面都不知道,要怎么找呢?那我就猜它在数组的正中间,即下标为 p = ⌊ n 2 ⌋ p=\lfloor\frac{n}{2}\rfloor p=⌊2n⌋的地方:
1. 如果运气够好,这个位置的元素恰好就是 X,即 a r r [ p ] = X arr[p]=X arr[p]=X,那直接返回 p p p 即可;
2. 如果运气不够好,这个位置的元素 a r r [ p ] > X arr[p]>X arr[p]>X,说明元素 X 在 p 的左边,因此将数组查找范围减半(把下标大于等于 p 的元素都忽略),只关注 arr[p - 1] 及之前的元素即可;
3. 如果这个位置的元素 a r r [ p ] < X arr[p]<X arr[p]<X,说明元素 X 在 p 的右边,同理,将数组查找范围减半(把下标小于等于 p 的元素都忽略),只关注 arr[p + 1] 及之后的元素即可。
经历一遍上面猜想的步骤,可以看到,问题又变得和原问题一样了:在一个缩小了的数组里寻找元素 X。不难得出下面的递归思路伪代码:
# arr: 升序数组
# X: 待查找元素
# start: 待查找数组的左边界(闭区间)
# end: 待查找数组的右边界(闭区间)
f(arr, X, start, end):if start > end:# 这里返回 -1 表示没找到return -1# 猜想X在数组中间位置mid = (start + end) / 2if arr[mid] == X:# 猜对了,直接返回下标return midif arr[mid] > X:# 没猜对,X在mid的左边,则朝左边寻找,将右界变小return f(arr, X, start, mid - 1)# 没猜对,X在mid的右边,则朝右边寻找,将左界变大return f(arr, X, mid + 1, end)
但是我们一般不喜欢递归解法,除非只有递归解法,不然的话都要看看能不能把递归转为迭代。上面的递归思路转为迭代还是很好办的,并且符合人的一般思维,如下:
f(arr, X):l = 0 # 待查找数组的左边界(闭区间),假设数组下标从0开始r = arr.length - 1 # 待查找数组的右边界(闭区间),假设数组下标从0开始while l <= r:mid = (l + r) / 2if arr[mid] == X:return midif arr[mid] > X:r = mid - 1;else:l = mid + 1;# 如果 X 存在,则一定会在循环中找到并返回,不会执行这一句,如果执行了这句,表示 X 不存在# 这里假设 X 不存在时返回 -1return -1
上面就是基本二分查找算法的思路,也是本题所考察的知识点。为什么要数组中的元素不重复呢?这是因为这里只考虑元素 X 在不在数组中,如果在则返回其下标,如果有多个 X 的话,那么应该返回哪个下标呢?随便返回一个下标一般是不合理的。所以就干脆直接限制数组元素不重复。
2.2 常用二分模板
如果是为了解决本题,那么上一小节就已经解决了。这一小节主要补充几个常用模板,这些模板可用于任何非递减数组(非递增数组同理)。
2.2.1 第一个大于等于目标值的元素下标
有一个非递减数组
arr,长度为n,查找arr中第一个大于等于X的元素下标,下标从0开始。
首先考虑两个特殊情况:
1. 如果 X 小于 arr 中的最小元素,则应该返回 0;
2. 如果 X 大于 arr 中的最大元素,则应该返回 n。
然后开始查找,不过和上面查找存不存在的思路有所区别:
1. 最中间那个下标 mid 处的值小于 X,说明答案在 mid 右边;
2. 最中间那个下标 mid 处的值大于等于 X,说明答案在 mid 左边。
由此可以得到上面问题的答案,直到最后,数组左边界即为答案。
模板见代码实现。
2.2.2 第一个大于目标值的元素下标
有一个非递减数组
arr,长度为n,查找arr中第一个大于X的元素下标,下标从0开始。
首先考虑两个特殊情况:
1. 如果 X 小于 arr 中的最小元素,则应该返回 0;
2. 如果 X 大于 arr 中的最大元素,则应该返回 n。
然后开始查找,不过和上面查找存不存在的思路有所区别:
1. 最中间那个下标 mid 处的值小于等于 X,说明答案在 mid 右边;
2. 最中间那个下标 mid 处的值大于 X,说明答案在 mid 左边。
由此可以得到上面问题的答案,直到最后,数组左边界即为答案。
模板见代码实现。
2.2.3 最后一个小于等于目标值的元素下标
有一个非递减数组
arr,长度为n,查找arr中最后一个小于等于X的元素下标,下标从0开始。
易知,最后一个小于等于目标值的元素下标 = 第一个大于目标值的元素下标 - 1
2.2.3 最后一个小于目标值的元素下标
有一个非递减数组
arr,长度为n,查找arr中最后一个小于X的元素下标,下标从0开始。
易知,最后一个小于目标值的元素下标 = 第一个大于等于目标值的元素下标 - 1
2.2.4 小结
上面的四种情况只需要实现两种情况即可,参照 C++ 的 lower_bound 和 upper_bound 函数。其余两种情况可以由另外两种情况转化而来。容易知道的是,前两种情况返回值的取值范围是 [0,n],后两种的返回值取值返回为 [-1,n-1],在应用二分查找时需要注意这些边界情况并小心处理。
3 代码实现
3.1 本题代码实现
3.1.1 递归法
Position BinarySearchHelp(List L, ElementType X, Position l, Position r) {if (l > r) return NotFound;Position mid = l + (r - l) / 2;if (L->Data[mid] == X) {return mid;}if (L->Data[mid] > X) {return BinarySearchHelp(L, X, l, mid - 1);}return BinarySearchHelp(L, X, mid + 1, r);
}Position BinarySearch(List L, ElementType X) {return BinarySearchHelp(L, X, 1, L->Last);
}
3.1.2 迭代法
Position BinarySearch(List L, ElementType X) {Position l = 1, r = L->Last, mid = 0;while (l <= r) {mid = l + (r - l) / 2;if (L->Data[mid] == X) {return mid;}if (L->Data[mid] > X) {r = mid - 1;} else {l = mid + 1;}}return NotFound;
}
3.2 常用模板
2.2.1 第一个大于等于目标值的元素下标
int first_ge(int* arr, int n, int target) {int l = 0, r = n - 1, mid = 0;while (l <= r) {mid = l + (r - l) / 2;if (arr[mid] >= target) {r = mid - 1;} else {l = mid + 1;}}return l;
}
2.2.2 第一个大于目标值的元素下标
int first_ge(int* arr, int n, int target) {int l = 0, r = n - 1, mid = 0;while (l <= r) {mid = l + (r - l) / 2;if (arr[mid] > target) {r = mid - 1;} else {l = mid + 1;}}return l;
}
4 总结
二分算法是一种思想,就像基本原理一样,原理很好懂,但是实现方法千变万化,喜好因人而异,上面所记录的都是一种 闭区间 的写法,即数组的左边界和右边界都是同时取得到的,另外还有 左闭右开 写法,比如 C++ 的 lower_bound 函数等,以及 开区间 等写法,其实原理就一个:二分,答案在哪边,就往哪边走。
相关文章:
【函数题】6-10 二分查找
6-10 二分查找 1 题目原文2 思路解析2.1 基本二分查找算法2.2 常用二分模板2.2.1 第一个大于等于目标值的元素下标2.2.2 第一个大于目标值的元素下标2.2.3 最后一个小于等于目标值的元素下标2.2.3 最后一个小于目标值的元素下标2.2.4 小结 3 代码实现3.1 本题代码实现3.1.1 递归…...
关于conda换镜像源,pip换源
目录 1. 查看当前下载源2. 添加镜像源2.1清华大学开源软件镜像站2.2上海交通大学开源镜像站2.3中国科学技术大学 3.删除镜像源4.删除所有镜像源,恢复默认5.什么是conda-forge6.pip换源 1. 查看当前下载源 conda config --show channels 如果发现多个 可以只保留1个…...
DeepSeek与ChatGPT的全面对比
在人工智能(AI)领域,生成式预训练模型(GPT)已成为推动技术革新的核心力量。OpenAI的ChatGPT自发布以来,凭借其卓越的自然语言处理能力,迅速占据市场主导地位。然而,近期中国AI初创公…...
Spring AI发布!让Java紧跟AI赛道!
1. 序言 在当今技术发展的背景下,人工智能(AI)已经成为各行各业中不可忽视的重要技术。无论是在互联网公司,还是传统行业,AI技术的应用都在大幅提升效率、降低成本、推动创新。从智能客服到个性化推荐,从语…...
基于CT107D单片机综合训练平台的秒表设计
1. 项目简介 在CT107D单片机综合训练平台上,利用定时器T0、数码管模块和2个独立按键(J5的2-3短接),设计一个具有清零、暂停、启动功能的秒表。秒表显示格式为:分-秒-0.05秒(即50ms),…...
opensuse [Linux] 系统挂在新的机械硬盘
opensuse [Linux] 系统挂在新的机械硬盘 需求描述 自用电脑型号如下: 电脑:Precision Tower 7810 (Dell Inc.) CPU : Intel Xeon CPU E5-2686 v4 2.30GHz GPU: NVIDIA GeForce GTX 1070 Linux版本:Linux version 6.…...
时间序列分析(四)——差分运算、延迟算子、AR(p)模型
此前篇章: 时间序列分析(一)——基础概念篇 时间序列分析(二)——平稳性检验 时间序列分析(三)——白噪声检验 一、差分运算 差分运算的定义:差分运算是一种将非平稳时间序列转换…...
【CUDA】Triton
【CUDA】Triton 1. CUDA 与 Triton 的基本区别 CUDA 编程模型: 在传统的 CUDA 编程中,CUDA 是标量程序,带有阻塞线程(blocked threads)。 标量程序(Scalar Program):表示我们直接…...
Windows环境搭建ES集群
搭建步骤 下载安装包 下载链接:https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.27-windows-x86_64.zip 解压 解压并复制出3份 es-node1配置 config/elasticsearch.yml cluster.name: xixi-es-win node.name: node-1 path.data: D:\\wor…...
langchain学习笔记之消息存储在内存中的实现方法
langchain学习笔记之消息存储在内存中的实现方法 引言背景消息存储在内存的实现方法消息完整存储:完整代码 引言 本节将介绍 langchain \text{langchain} langchain将历史消息存储在内存中的实现方法。 背景 在与大模型交互过程中,经常出现消息管理方…...
怎么在智能合约中植入deepseek
怎么在智能合约中植入deepseek 这里写目录标题 怎么在智能合约中植入deepseek方法概述具体步骤1. 部署大语言模型到链下2. 创建预言机(Oracle)a. 部署预言机节点b. 创建自定义预言机接口(Custom Oracle)3. 设计智能合约a. 编写Solidity代码b. 部署智能合约4. 调用流程注意事…...
驱动开发系列37 - Linux Graphics 2D 绘制流程(二)- 画布创建和窗口关联
一:概述 前面介绍Pixmap表示一块画布,是绘制发生的地方,本节看看驱动程序如何为画布分配内存/显存,以及如何与窗口关联的。 二:为画布分配BO 在系统启动时(用户登录系统之后,会重启Xorg),在 Xorg 服务器初始化时,要为屏幕创建根窗口的 Pixmap,并绑定到 GPU framebu…...
B. Longest Divisors Interval
time limit per test 2 seconds memory limit per test 256 megabytes Given a positive integer nn, find the maximum size of an interval [l,r][l,r] of positive integers such that, for every ii in the interval (i.e., l≤i≤rl≤i≤r), nn is a multiple of ii. …...
前端与后端的对接事宜、注意事项
前端与后端的对接事宜、注意事项 一、对接核心流程(完整生命周期) #mermaid-svg-6yzij6OD8DKqiMLD {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6yzij6OD8DKqiMLD .error-icon{fill:#552222;}#mermaid-svg-6yzi…...
【第13章:自监督学习与少样本学习—13.2 少样本学习(FSL)与元学习(Meta-Learning)的基础理论与应用案例】
凌晨三点的急诊室,值班医生李大夫正在使用AI辅助诊断系统——面对一张仅有3个标注病例的罕见皮肤病影像,系统竟然给出了95%置信度的准确诊断。这种"见微知著"的超能力,正是少样本学习技术创造的医学奇迹。 一、突破数据荒漠:少样本学习的生存法则 1.1 从人类学习…...
函数防抖和节流
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次, 如果在 n 秒内又触发了事件,则会重新计算函数执行时间, 短时间高频率触发只有最后一次触发成功 开发使用场景: 搜索框防抖 fn代表要被防抖或者节流的函数&#x…...
linux--关于linux文件IO(2) open、read、lseek、stat
open 在linux中的读写文件有对应的命令。在终端中输入man 2 open可以打开open的手册页,注意man 2是linux自己的函数的一些手册,man 3是C库的手册 打开手册页之后找到open函数的用法如下: #以下是需要的库文件,man 2 open打开直接…...
利用xtquant高效获取财务数据:量化分析的重要补充
利用xtquant高效获取财务数据:量化分析的重要补充 在量化交易领域,虽然市场行情数据是核心,但财务数据作为企业基本面的重要反映,同样不可忽视。通过深入分析企业的财务报表,投资者可以更好地理解企业的经营状况和未来…...
Unity UI个人总结
个人总结,太简单的直接跳过。 一、缩放模式 1.固定像素大小 就是设置一个100x100的方框,在1920x1080像素下在屏幕中长度占比1/19,在3840x2160,方框在屏幕中长度占比1/38。也就是像素长款不变,在屏幕中占比发生变化 2.…...
Javascript的数据类型
Javascript的数据类型 1.基本数据类型1.1七种基本数据类型1.2单独说说BigInt1.3其它注意点 2.引用数据类型3.基本数据类型和引用数据类型的区别4.双等于号和三等于号的区别5.Javascript的类型转换机制5.1显示转换(强制转换)5.2隐式转换(1)减、乘、除(2)加(加法要区别算,因为不…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
UniApp 集成腾讯云 IM 富媒体消息全攻略(地理位置/文件) 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型,核心实现方式: 标准消息类型:直接使用 SDK 内置类型(文件、图片等)自…...
ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
数据分析六部曲?
引言 上一章我们说到了数据分析六部曲,何谓六部曲呢? 其实啊,数据分析没那么难,只要掌握了下面这六个步骤,也就是数据分析六部曲,就算你是个啥都不懂的小白,也能慢慢上手做数据分析啦。 第一…...
