当前位置: 首页 > news >正文

【函数题】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 要查找 XData 中的位置,即数组下标(注意:元素从下标 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,说明元素 Xp 的左边,因此将数组查找范围减半(把下标大于等于 p 的元素都忽略),只关注 arr[p - 1] 及之前的元素即可;
        3. 如果这个位置的元素 a r r [ p ] < X arr[p]<X arr[p]<X,说明元素 Xp 的右边,同理,将数组查找范围减半(把下标小于等于 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_boundupper_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.删除所有镜像源&#xff0c;恢复默认5.什么是conda-forge6.pip换源 1. 查看当前下载源 conda config --show channels 如果发现多个 可以只保留1个…...

DeepSeek与ChatGPT的全面对比

在人工智能&#xff08;AI&#xff09;领域&#xff0c;生成式预训练模型&#xff08;GPT&#xff09;已成为推动技术革新的核心力量。OpenAI的ChatGPT自发布以来&#xff0c;凭借其卓越的自然语言处理能力&#xff0c;迅速占据市场主导地位。然而&#xff0c;近期中国AI初创公…...

Spring AI发布!让Java紧跟AI赛道!

1. 序言 在当今技术发展的背景下&#xff0c;人工智能&#xff08;AI&#xff09;已经成为各行各业中不可忽视的重要技术。无论是在互联网公司&#xff0c;还是传统行业&#xff0c;AI技术的应用都在大幅提升效率、降低成本、推动创新。从智能客服到个性化推荐&#xff0c;从语…...

基于CT107D单片机综合训练平台的秒表设计

1. 项目简介 在CT107D单片机综合训练平台上&#xff0c;利用定时器T0、数码管模块和2个独立按键&#xff08;J5的2-3短接&#xff09;&#xff0c;设计一个具有清零、暂停、启动功能的秒表。秒表显示格式为&#xff1a;分-秒-0.05秒&#xff08;即50ms&#xff09;&#xff0c…...

opensuse [Linux] 系统挂在新的机械硬盘

opensuse [Linux] 系统挂在新的机械硬盘 需求描述 自用电脑型号如下&#xff1a; 电脑&#xff1a;Precision Tower 7810 (Dell Inc.) CPU &#xff1a; Intel Xeon CPU E5-2686 v4 2.30GHz GPU&#xff1a; NVIDIA GeForce GTX 1070 Linux版本&#xff1a;Linux version 6.…...

时间序列分析(四)——差分运算、延迟算子、AR(p)模型

此前篇章&#xff1a; 时间序列分析&#xff08;一&#xff09;——基础概念篇 时间序列分析&#xff08;二&#xff09;——平稳性检验 时间序列分析&#xff08;三&#xff09;——白噪声检验 一、差分运算 差分运算的定义&#xff1a;差分运算是一种将非平稳时间序列转换…...

【CUDA】Triton

【CUDA】Triton 1. CUDA 与 Triton 的基本区别 CUDA 编程模型&#xff1a; 在传统的 CUDA 编程中&#xff0c;CUDA 是标量程序&#xff0c;带有阻塞线程&#xff08;blocked threads&#xff09;。 标量程序&#xff08;Scalar Program&#xff09;&#xff1a;表示我们直接…...

Windows环境搭建ES集群

搭建步骤 下载安装包 下载链接&#xff1a;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学习笔记之消息存储在内存中的实现方法 引言背景消息存储在内存的实现方法消息完整存储&#xff1a;完整代码 引言 本节将介绍 langchain \text{langchain} langchain将历史消息存储在内存中的实现方法。 背景 在与大模型交互过程中&#xff0c;经常出现消息管理方…...

怎么在智能合约中植入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 从人类学习…...

函数防抖和节流

所谓防抖&#xff0c;就是指触发事件后在 n 秒内函数只能执行一次&#xff0c; 如果在 n 秒内又触发了事件&#xff0c;则会重新计算函数执行时间&#xff0c; 短时间高频率触发只有最后一次触发成功 开发使用场景&#xff1a; 搜索框防抖 fn代表要被防抖或者节流的函数&#x…...

linux--关于linux文件IO(2) open、read、lseek、stat

open 在linux中的读写文件有对应的命令。在终端中输入man 2 open可以打开open的手册页&#xff0c;注意man 2是linux自己的函数的一些手册&#xff0c;man 3是C库的手册 打开手册页之后找到open函数的用法如下&#xff1a; #以下是需要的库文件&#xff0c;man 2 open打开直接…...

利用xtquant高效获取财务数据:量化分析的重要补充

利用xtquant高效获取财务数据&#xff1a;量化分析的重要补充 在量化交易领域&#xff0c;虽然市场行情数据是核心&#xff0c;但财务数据作为企业基本面的重要反映&#xff0c;同样不可忽视。通过深入分析企业的财务报表&#xff0c;投资者可以更好地理解企业的经营状况和未来…...

Unity UI个人总结

个人总结&#xff0c;太简单的直接跳过。 一、缩放模式 1.固定像素大小 就是设置一个100x100的方框&#xff0c;在1920x1080像素下在屏幕中长度占比1/19&#xff0c;在3840x2160&#xff0c;方框在屏幕中长度占比1/38。也就是像素长款不变&#xff0c;在屏幕中占比发生变化 2.…...

Javascript的数据类型

Javascript的数据类型 1.基本数据类型1.1七种基本数据类型1.2单独说说BigInt‌1.3其它注意点 2.引用数据类型3.基本数据类型和引用数据类型的区别4.双等于号和三等于号的区别5.Javascript的类型转换机制5.1显示转换(强制转换)5.2隐式转换(1)减、乘、除(2)加(加法要区别算,因为不…...

面试官最爱问的Java多线程问题,你掌握了吗?

在当今软件开发领域&#xff0c;多线程编程已成为衡量一个开发者技术水平的重要标准之一。特别是在Java这一广泛使用的编程语言中&#xff0c;多线程能力更是面试官们青睐的考察点。掌握好Java多线程&#xff0c;不仅能提升程序性能&#xff0c;还能让你在众多求职者中脱颖而出…...

去中心化数据同步:构建自主可控的Any-Sync系统

1. 项目概述&#xff1a;从“同步一切”到“掌控一切”的进化在数字生活的日常里&#xff0c;我们每个人都被困在无数个“信息孤岛”中。工作文档躺在公司的云盘&#xff0c;个人照片塞满了手机相册&#xff0c;读书笔记散落在不同的App&#xff0c;而浏览器书签则随着设备切换…...

SAP ABAP实战:用BAPI_PR_CHANGE批量更新采购申请,别再一条条改了

SAP ABAP高效开发&#xff1a;BAPI_PR_CHANGE批量处理采购申请的工程化实践 采购申请&#xff08;Purchase Requisition&#xff09;作为企业采购流程的起点&#xff0c;其数据维护效率直接影响采购部门的运作效能。当面对数百甚至上千条需要同步更新文本、状态或关键字段的采购…...

iOS原生AI助手开发实战:从UIKit选型到Stable Diffusion本地部署

1. 项目概述&#xff1a;一个原生、全能的iOS端AI助手最近在App Store上架了一款名为“Chat走啦”的iOS应用&#xff0c;它本质上是一个功能相当全面的ChatGPT原生客户端。和很多基于WebView简单套壳的应用不同&#xff0c;这个项目从底层网络请求到上层UI交互&#xff0c;都采…...

避坑指南:ESP32用Modbus读485设备,为什么你的软串口总收不到数据?

ESP32 Modbus通信避坑指南&#xff1a;软串口数据丢失的深层分析与解决方案 当你在ESP32项目中使用Modbus协议通过485接口读取传感器数据时&#xff0c;是否遇到过这样的场景&#xff1a;硬件连接正确&#xff0c;代码看似无误&#xff0c;但软串口(SoftwareSerial)就是收不到任…...

终极免费音乐解锁工具:3步完成加密音乐文件本地解密

终极免费音乐解锁工具&#xff1a;3步完成加密音乐文件本地解密 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库&#xff1a; 1. https://github.com/unlock-music/unlock-music &#xff1b;2. https://git.unlock-music.dev/um/web 项目地址: https:/…...

维普AIGC率过高怎么解?双效工具同步搞定查重与AI痕迹

毕业季双重检测压力陡增&#xff0c;不少同学熬夜反复改稿&#xff0c;维普查重标红迟迟消不掉&#xff0c;AIGC疑似率更是居高不下&#xff0c;越改越乱不说&#xff0c;还容易打乱论文核心逻辑。其实完全不用死磕手动改写&#xff0c;现在多款专业双效降重工具已经能实现“一…...

告别跷跷板效应:手把手教你用PaddlePaddle复现腾讯PLE多任务推荐模型

从零实现腾讯PLE模型&#xff1a;用PaddlePaddle解决多任务推荐中的跷跷板难题 推荐系统发展到今天&#xff0c;早已不再是简单的协同过滤或矩阵分解就能满足业务需求。当我们需要同时优化点击率、观看时长、分享率等多个目标时&#xff0c;传统的单任务学习模型往往捉襟见肘。…...

7+ Taskbar Tweaker疑难杂症终极指南:从症状到根除的完整解决方案

7 Taskbar Tweaker疑难杂症终极指南&#xff1a;从症状到根除的完整解决方案 【免费下载链接】7-Taskbar-Tweaker A Windows taskbar customization tool for Windows 7, Windows 8, and Windows 10 项目地址: https://gitcode.com/gh_mirrors/7t/7-Taskbar-Tweaker 7 T…...

从“解决”到“消解”:电车难题作为AI元人文的第一次工程实验

从“解决”到“消解”&#xff1a;电车难题作为AI元人文的第一次工程实验摘要传统自动驾驶伦理试图回答“算法应当如何选择”——本质上是旧主体结构内的规则修补。本文基于一篇题为《电车难题的一个原创解决方案》的博客&#xff0c;揭示其未被广泛识别的前提&#xff1a;该方…...