二分查找代码详解
二分查找代码实现
以下是完整的代码和解释:
#include <stdio.h>int binarySearch(int arr[], int length, int target) {int left = 0;int right = length - 1;while (left <= right) {int mid = left + (right - left) / 2; // 防止溢出if (arr[mid] == target) {return mid; // 找到目标元素}if (arr[mid] < target) {left = mid + 1; // 目标在右半部分} else {right = mid - 1; // 目标在左半部分}}return -1; // 未找到目标元素
}int main() {int arr[] = {1, 3, 5, 7, 9, 11};int target = 7;int length = sizeof(arr) / sizeof(arr[0]); // 在主函数中计算数组长度int result = binarySearch(arr, length, target);if (result == -1) {printf("元素未找到\n");} else {printf("元素找到了,位置为: %d\n", result);}return 0;
}
在上面的代码中,使用 int mid = left + (right - left) / 2; 代替 int mid = (left + right) / 2; 是为了防止整数溢出。具体原因和解释如下:
原因
假设 left 和 right 都是非常大的正整数,那么 left + right 可能会超过 int 类型的表示范围,导致整数溢出。然而, left + (right - left) / 2 通过先计算 (right - left) / 2,确保了不会超过 int 的表示范围。
例子
假设 left = 2147483640 和 right = 2147483646:
left + right的值是4294967286,超过了int的最大值2147483647,会导致溢出。right - left的值是6,计算(right - left) / 2得到3,然后left + 3的结果是2147483643,没有溢出。
代码执行流程
-
初始化:
left初始化为数组的起始索引0。right初始化为数组的末尾索引length - 1。
-
二分查找循环:
- 计算中间位置
mid,使用int mid = left + (right - left) / 2;防止溢出。 - 检查
arr[mid]是否等于目标值target。如果相等,返回mid。 - 如果
arr[mid]小于目标值target,将left更新为mid + 1,继续在右半部分查找。 - 如果
arr[mid]大于目标值target,将right更新为mid - 1,继续在左半部分查找。
- 计算中间位置
-
返回结果:
- 如果找到了目标元素,返回其索引。
- 如果循环结束后没有找到目标元素,返回
-1。
总结
通过使用 int mid = left + (right - left) / 2;,你可以有效避免由于 left 和 right 的和超出 int 范围导致的溢出问题,确保二分查找在所有情况下都能正确执行。
在数学上证明 left + (right - left) / 2 和 (left + right) / 2 得到的结果是相同的。两者之所以相等,归根结底在于它们的计算方式和中间步骤是相同的。
证明过程
令 ( L ) 表示 left,( R ) 表示 right。
-
表达式1:
(left + right) / 2
mid = L + R 2 \text{mid} = \frac{L + R}{2} mid=2L+R -
表达式2:
left + (right - left) / 2
mid = L + R − L 2 \text{mid} = L + \frac{R - L}{2} mid=L+2R−L
为了证明这两个表达式是等价的,我们可以将表达式2进行展开和简化:
L + R − L 2 = L + R 2 − L 2 = 2 L 2 + R 2 − L 2 = L + R 2 L + \frac{R - L}{2} = L + \frac{R}{2} - \frac{L}{2} = \frac{2L}{2} + \frac{R}{2} - \frac{L}{2} = \frac{L + R}{2} L+2R−L=L+2R−2L=22L+2R−2L=2L+R
由此可见,两个表达式是相等的。
原理
在数学上,两者的计算结果是一样的,因为加法和减法在整数范围内是可交换和结合的。然而在计算机科学中,特别是在考虑可能存在的整数溢出问题时,使用 left + (right - left) / 2 更为安全。
整数溢出问题
假设 left 和 right 都是非常大的整数,如果直接计算 (left + right) / 2,有可能导致 left + right 超出整数范围,产生溢出。
比如,在 32 位系统中,最大整数值为 2147483647。如果 left 和 right 的和超过这个值,就会发生溢出,从而导致计算结果错误。
通过 left + (right - left) / 2 这种方式,避免了直接相加可能带来的溢出问题,因为 right - left 的结果不会超过原来的范围。
实际代码示例
#include <iostream>
#include <limits.h>int main() {int left = INT_MAX - 1;int right = INT_MAX;// 计算两种方式的结果int mid1 = (left + right) / 2;int mid2 = left + (right - left) / 2;std::cout << "Using (left + right) / 2: " << mid1 << std::endl;std::cout << "Using left + (right - left) / 2: " << mid2 << std::endl;return 0;
}
结果验证
当 left 和 right 都接近 INT_MAX 时,(left + right) / 2 可能会导致溢出,而 left + (right - left) / 2 则可以避免这个问题,得到正确的结果。
通过这种方式,我们不仅证明了两者的数学等价性,还理解了在编程中为什么更推荐使用 left + (right - left) / 2 来避免潜在的整数溢出问题。
相关文章:
二分查找代码详解
二分查找代码实现 以下是完整的代码和解释: #include <stdio.h>int binarySearch(int arr[], int length, int target) {int left 0;int right length - 1;while (left < right) {int mid left (right - left) / 2; // 防止溢出if (arr[mid] target…...
uniapp的h5,读取本地txt带标签的文件
效果图 使用的回显的标签是u-parse,下面的网址讲了这个标签的相关 https://www.cnblogs.com/huihuihero/p/12978903.html 导入此插件 https://ext.dcloud.net.cn/plugin?id364 使用 uni.request({// 本地文件url: "/static/互联网医院医师端用户协议.txt…...
韦东山嵌入式linux系列-具体单板的按键驱动程序(查询方式)
1 GPIO 操作回顾 (1)使能模块; (2)设置引脚的模式(工作于GPIO模式); (3)设置GPIO本身(输入/输出); (4&…...
如何使用 API list 极狐GitLab 群组中的镜像仓库?
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab :https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署…...
PHP设计模式-简单工厂模式
核心: 一、定义一个接口类里面写规定好的方法。 interface Message{public function send(array $params);public function getMessage(array $params);public function getCode(array $params);} 二、定义产品类 、产品类继承接口类 class AlliYunSms implements …...
C语言航空售票系统
以下是系统部分页面 以下是部分源码,需要源码的私信 #include<stdio.h> #include<stdlib.h> #include<string.h> #define max_user 100 typedef struct ft {char name[50];//名字char start_place[50];//出发地char end_place[50];//目的地char …...
Oracle 19c打Datapatch数据补丁报错处理
Oracle 19c打Datapatch数据补丁报错处理 错误分析重新编译补丁验证安装完数据库补丁后,在数据补丁的步骤收到以下报错: Connecting to database...OK Gathering database info...done Bootstrapping registry and package to current versions...done Determining current s…...
Linux shell编程学习笔记66:ping命令 超详细的选项说明
0 前言 网络信息是电脑网络信息安全检查中的一块重要内容,Linux和基于Linux的操作系统,提供了很多的网络命令,今天我们研究最常用的ping命令。 1 ping命令 的功能、格式和选项说明 1.1 ping命令 的功能 简单来说, ping 命令 会…...
SSL/TLS和SSL VPN
1、SSL/TLS SSL安全套接字层:是一种加密协议,用于在网络通信中建立安全连接。它在应用层和传输层(TCP/IP)之间提供数据加密、服务器身份验证以及信息完整性验证 SSL只保护TCP流量,不保护UDP协议 TLS:传输层…...
浅谈WebSerice
一. 什么是WebService Web Service也称为web服务,它是一种跨编程语言和操作系统平台的远程调用技术。Web Service采用标准的SOAP协议传输(SOAP:Simple Object Access Protocol简单对象访问协议,soap属于w3c标准。并且soap协议是基…...
linux快速入门-学习笔记
linux快速入门-学习笔记 第一章:Linux系统概念及命令学习Linux系统基本概念命令终端介绍命令格式介绍Linux系统辨别目录与文件的方法通过文件详细属性辨别ls 查看目录/文件命令Linux 系统下的归属关系命令行编辑技巧Linux 基本权限的类别课后练习 第二章:…...
科普文:5种Linux下软件部署方式说明
在Linux世界里,高效、灵活地安装和管理软件是每个系统管理员和开发者的基本功。从传统的RPM包管理,到便捷的YUM软件仓库,再到颠覆性的Docker容器技术,Snap,源码安装,每一种方法都有其独到之处,适…...
Redisson中的RBlockingQueue的使用场景及例子
Redisson 的 RBlockingQueue 是一个实现了 Java BlockingQueue 接口的分布式队列,它可以用于在分布式系统中实现生产者-消费者模式。RBlockingQueue 提供了线程安全的阻塞队列操作,允许生产者在队列满时阻塞,消费者在队列空时阻塞,…...
【办公软件】Office 2019以上版本PPT 做平滑切换
Office2019以上版本可以在切页面时做平滑切换,做到一些简单的动画效果。如下在快捷菜单栏中的切换里选择平滑。 比如,在两页PPT中,使用同一个形状对象,修改了大小和颜色。 选择切换为平滑后,可以完成如下的动画显示。 …...
connect-multiparty中间件用法以及实例--文件上传中间件(保姆级别教学)
connect-multiparty中间件的用法包括安装和引入、基本设置、路由应用、文件处理以及安全和优化等步骤。 connect-multiparty是一个专为Connect和Express框架设计的文件上传中间件,它基于multiparty库,用于处理多部分表单数据,尤其针对文件上传…...
0503触发器的电路结构和工作原理
触发器的电路结构和工作原理 如何区分锁存器还是触发器, 看有没有这个三角符号,告诉是上升沿触发还是下降沿触发,没有三角符号就是电平触发。低电平触发就画个小圈。高电平触发就不画小圈。有小圈的三角就是下降沿触发 setup建立时间 hold 保…...
LeetCode:二叉树的中序遍历(C语言)
1、前序遍历:根左右 2、中序遍历:左根右 3、后序遍历:左右根 1、问题概述:二叉树中序遍历 2、示例 示例 1: 输入:root [1,null,2,3] 输出:[1,3,2] 示例 2: 输入:root […...
MySQL数据库基本安装与部署
目录 概念 数据库的基本概念 关系型数据库 非关系型数据库 MySQL 商业版与社区版 示例 初始化MySQL 添加系统服务 概念 数据库的基本概念 数据(Data) 描述事物的符号记录包括数字、文字、图形、图像、声音、档案记录等以“记录”形式按统一的…...
paraFoam 运行 报错 usr/lib/x86_64-linux-gnu/libQt5Core.so 已解决
在日常项目开发中。使用ubuntu 视图开发的时候。报错 缺少 libQt5Core 核心组件! whereis libQt5Core.so.5sudo strip --remove-section.note.ABI-tag /usr/lib/x86_64-linux-gnu/libQt5Core.so.5 完美解决,并且能正常打开,前提是,…...
科技前沿:Llama 3.1的突破与革新
在科技的长河中,每一次模型的更新都是对人类智慧的致敬。今天,我们将聚焦于Meta公司最新发布的Llama 3.1系列模型,探索其在AI领域的前沿突破。 新模型的诞生 自去年以来,Meta公司不断推进人工智能技术的发展,终于在近…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...
pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决
问题: pgsql数据库通过备份数据库文件进行还原时,如果表中有自增序列,还原后可能会出现重复的序列,此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”,…...
零基础在实践中学习网络安全-皮卡丘靶场(第十一期-目录遍历模块)
经过前面几期的内容我们学习了很多网络安全的知识,而这期内容就涉及到了前面的第六期-RCE模块,第七期-File inclusion模块,第八期-Unsafe Filedownload模块。 什么是"遍历"呢:对学过一些开发语言的朋友来说应该知道&…...
