一篇文章带你学会“二分算法”

二分算法(也称为二分法或折半查找)是一种在有序数组中查找特定元素的搜索算法。其基本原理是通过不断缩小查找范围来逼近目标值。以下是二分算法的详细讲解:

基本原理

  1. 有序性:二分算法要求待搜索的数组必须是有序的(通常是升序或降序)。
  2. 缩小范围:算法通过比较目标值与数组中间元素的大小,将搜索范围缩小为原范围的一半。
  3. 循环迭代:重复上述过程,直到找到目标值或确定目标值不存在为止。

实现步骤

  1. 确定初始范围:根据数组的大小,确定初始的搜索范围(通常是数组的起始和结束索引)。
  2. 计算中间位置:通过计算范围的中间位置(通常使用(left + right) / 2left + (right - left) / 2以避免整数溢出),得到中间元素的索引。
  3. 比较中间元素:将目标值与中间元素进行比较。
    • 如果目标值等于中间元素,则查找成功,返回中间元素的索引。
    • 如果目标值小于中间元素,则在数组的左半部分继续查找(更新右边界为mid - 1)。
    • 如果目标值大于中间元素,则在数组的右半部分继续查找(更新左边界为mid + 1)。
  4. 循环迭代:重复步骤2和3,直到找到目标值或搜索范围为空(即left > right)。

时间复杂度

二分算法的时间复杂度为O(log n),其中n是数组的长度。这意味着随着数据量的增加,其查找时间并不会呈线性增长,而是呈对数增长。因此,二分算法是一种非常高效的搜索算法,尤其适用于大规模数据的查找。

适用范围

二分算法适用于解决具有“二段性”(单调性)的问题,通常表现为求解满足某一条件的最大值或最小值问题。例如,在有序数组中查找某个特定的值、在范围内查找满足特定条件的数据等。


下面是一个使用Java编写的二分查找算法的详细示例。这个算法假设我们要在一个已排序的整数数组中查找一个特定的整数。

public class BinarySearch {  // 二分查找算法  public static int binarySearch(int[] arr, int target) {  if (arr == null || arr.length == 0) {  // 如果数组为空,返回-1表示未找到  return -1;  }  int left = 0; // 搜索范围的左边界  int right = arr.length - 1; // 搜索范围的右边界  while (left <= right) {  // 计算中间位置,防止溢出  int mid = left + (right - left) / 2;  // 检查目标值是否在中间位置  if (arr[mid] == target) {  // 如果找到,返回中间位置的索引  return mid;  } else if (arr[mid] < target) {  // 如果目标值大于中间值,搜索右半部分  left = mid + 1;  } else {  // 如果目标值小于中间值,搜索左半部分  right = mid - 1;  }  }  // 如果循环结束仍未找到,返回-1表示未找到  return -1;  }  // 测试方法  public static void main(String[] args) {  int[] arr = {2, 3, 4, 10, 40}; // 已排序的数组  int target = 10; // 要查找的目标值  int result = binarySearch(arr, target);  if (result == -1) {  System.out.println("目标值 " + target + " 未在数组中找到");  } else {  System.out.println("目标值 " + target + " 在数组中的索引为 " + result);  }  }  
}

 重点问题:这里的循环条件为什么是left <= right而不是left < right

  1. 确保搜索范围有效,防止遗漏检查
    • 当 left 指向数组的起始位置,而 right 指向数组的末尾位置时,搜索范围是有效的。
    • 如果我们使用 left < right 作为条件,那么在最后一次迭代中,当 left 和 right 相等时,循环就会结束,但我们还没有检查这个共同的索引位置是否包含目标值。
    • 只有在 left 和 right 相等时,我们才能确定搜索范围内已经没有更多的元素可以检查,因此可以安全地返回未找到的结果。

变形来啦! 

如果我特意把int right = arr.length - 1; 改为int right = arr.length ; 代码又该如何书写

当将 right 初始化为 arr.length 时,right 本身并不表示一个有效的数组索引(因为有效的索引是从0到arr.length - 1)。因此,在循环内部,你永远不会直接使用 right 作为索引来访问数组元素。相反,你会使用计算出的中间索引 mid

将循环条件更改为 left < right 是为了确保在循环过程中 left 和 right 不会相等,除非它们已经收敛到同一个位置(即目标值应该存在的位置,或者如果目标值不存在,则它们收敛到插入点)。

在标准的二分查找中,当 left == right 时,我们检查 arr[left](或 arr[right],因为它们此时是相等的)是否等于目标值。但是,在你的特定情况下,因为 right 被初始化为 arr.length,所以你不能直接检查 arr[right]。因此,你需要在循环继续执行直到 left < right,并且在循环结束后单独检查 arr[left] 是否等于目标值(如果循环因 left == right 而结束)。

public static int binarySearch(int[] arr, int target) {  if (arr == null || arr.length == 0) {  return -1;  }  int left = 0;  int right = arr.length; // 注意这里是arr.length,但我们不会直接用它来索引  while (left < right) { // 注意循环条件变为left < right  int mid = left + (right - left) / 2; // 计算中间索引时仍然安全  if (arr[mid] == target) {  return mid;  } else if (arr[mid] < target) {  left = mid + 1;  } else {  right = mid; // 注意这里将right设置为mid,而不是mid - 1  }  }  // 循环结束后,left == right,如果目标值在数组中,它应该在left/right-1的位置  // 但因为我们没有检查left/right-1,所以返回-1表示未找到  return -1;  
}

如果有序数组中连续有几个数相同,我要返回最左边的索引,代码又该如何书写

public static int binarySearchLeftmost(int[] arr, int target) {  if (arr == null || arr.length == 0) {  return -1;  }  int left = 0;  int right = arr.length - 1;  int result = -1; // 初始化结果为-1,表示未找到  while (left <= right) {  int mid = left + (right - left) / 2;  if (arr[mid] >= target) { // 注意这里使用 >= 而不是 ==  // 如果找到了目标值或者中间值比目标值大(因为是有序数组),则向左搜索  result = mid; // 先保存当前索引,因为我们可能还没找到最左边的  right = mid - 1; // 继续向左搜索  } else {  left = mid + 1; // 向右搜索  }  }  // 如果result没有被更新过,说明没有找到目标值  return result;  
}

在这个实现中,我们使用了 >= 来判断中间值是否大于或等于目标值。这是因为当找到目标值时,我们想要继续向左搜索以找到最左边的索引。如果 arr[mid] 等于 target,我们更新 result 并将 right 设置为 mid - 1 以继续向左搜索。如果 arr[mid] 大于 target,我们同样向左搜索。如果 arr[mid] 小于 target,我们则向右搜索。

当循环结束时,result 将包含最左边的目标值的索引(如果存在的话),或者如果没有找到目标值,则 result 将保持为初始值 -1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/31913.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在下游市场需求带动下 我国气调包装机市场规模逐渐扩大

在下游市场需求带动下 我国气调包装机市场规模逐渐扩大 气调包装机又称为气调保鲜包装机&#xff0c;是一种具有气体置换功能的保鲜包装设备。气调包装机的工作原理是将原有的包装内空气抽至真空&#xff0c;再充入一定配比的混合气体&#xff0c;从而对被包装的物品进行有效保…

ubuntu开机怎么进入、退出命令行界面

要在Ubuntu系统开机时进入命令行界面&#xff0c;可以按照以下步骤操作&#xff1a; 在开机过程中按下Ctrl Alt F1组合键&#xff0c;这将会切换到第一个虚拟控制台&#xff0c;即命令行界面。如果Ctrl Alt F1没有生效&#xff0c;也可以尝试Ctrl Alt F2、Ctrl Alt F3…

点云处理中阶 Sampling

目录 一、什么是点云Sampling 二、示例代码 1、下采样 Downsampling 2、均匀采样 3、上采样 4、表面重建 一、什么是点云Sampling 点云处理中的采样(sampling)是指从大量点云数据中选取一部分代表性的数据点,以减少计算复杂度和内存使用,同时保留点云的几何特征和重…

Java module-info模块系统

开源项目SDK&#xff1a;https://github.com/mingyang66/spring-parent 个人文档&#xff1a;https://mingyang66.github.io/raccoon-docs/#/ 从Java9开始引入了模块系统&#xff08;Jigsaw项目&#xff09;&#xff0c;用于更好的管理代码依赖和封装性。模块系统允许你定义模块…

python-爬虫篇-爬取百度贴吧,段友之家的图片和视频

#!/usr/bin/env python # -*- coding: utf-8 -*-""" 爬取百度贴吧&#xff0c;段友之家的图片和视频 author: cuizy time&#xff1a;2018-05-19 """import requests import bs4 import osdef write_file(file_url, file_type):""&quo…

02 Shell编程之条件语句

1、条件测试操作 要使Shell脚本程序具备一定的智能&#xff0c;面临的第一个问题就是如何区分不同的情况以确定执行何种操作。 例如&#xff0c;当磁盘使用率超过95%时&#xff0c;发送告警信息&#xff1b;当备份目录不存在时&#xff0c;能够自动创建&#xff1b; 当源码编…

深入解析:银行信贷业务办理的核心流程

一、引言 银行信贷管理是一个复杂而严谨的过程&#xff0c;它涉及从贷款申请到贷款归还的每一个环节。通过科学、审慎、合规的信贷管理&#xff0c;银行能够确保资金的安全性&#xff0c;降低风险&#xff0c;并提供高效的信贷服务。本文将详细揭秘银行信贷业务办理的主要业务流…

Qt之文件操作(QFile、QFileInfo、QTemporaryFile)

文章目录 前言QFile如何使用 QFile QFileInfo如何使用 QFileInfo QTemporaryFile如何使用 QTemporaryFile QFile常用函数QFileInfo常用函数QTemporaryFile常用函数总结 前言 在开发 Qt 应用程序时&#xff0c;我们经常需要进行文件操作&#xff0c;如读取文件、写入文件、获取…

超大cvs文件导入MySQL

1 XXX.cvs 太大 使用cvs拆分HugeCSVSplitter_jb51工具进行拆分&#xff0c;Line Count 设置为1,000,000 注意&#xff1a;1 拆分后除第一个子cvs文件含有标题外&#xff0c;其他的子文档都不含有标题行&#xff1b; 2 后一个文档的第一行为前一个文档的…

Automa 插件

插件下载 (Version:1.18.1)&#xff1a;https://download.csdn.net/download/code_stream/89467293视频教程1&#xff1a;https://www.bilibili.com/video/BV19VTueJESA/视频教程2&#xff1a;https://www.bilibili.com/list/36751867

小抄 20240618

1 有些人只要看到一件事有难度&#xff0c;内心就会觉得自己做不到&#xff0c;很容易放弃。 有难度和做不到&#xff0c;是两回事。 让你不做任何改变&#xff0c;银行卡凭空多出一百万&#xff0c;这个做不到。 让你定个十年计划&#xff0c;凭个人能力存到一百万&#xf…

MyBatis系列六: 映射关系多对一

动态SQL语句-更复杂的查询业务需求 官方文档基本介绍映射方式配置Mapper.xml的方式-应用实例注解的方式实现-应用实例课后练习 官方文档 文档地址: https://mybatis.org/mybatis-3/zh_CN/sqlmap-xml.html 基本介绍 ●基本介绍 1.项目中多对1的关系是一个基本的映射关系, 也可…

ITSS信息技术服务标准是什么?

ITSS运维模型规定了各级运维服务能力成熟度在管理、人员、过程、技术和资源方面应满足的要求。适用于运维服务供方建立、保持和改进运维服务能力&#xff0c;也适用于评价供方运维服务能力。 运维服务能力成熟度模型按运维服务组织能力建设和管理定义了逐步进化的四个等级&…

领先GPT-4o:Anthropic 推出新一代模型 Claude 3.5 Sonnet|TodayAI

Anthropic&#xff0c;全球领先的人工智能实验室之一&#xff0c;近日发布了其最新的人工智能模型——Claude 3.5 Sonnet。该模型不仅速度更快&#xff0c;成本更低&#xff0c;而且在多个关键任务上的表现超过了其前代模型 Claude 3 Opus。 更强的视觉功能与幽默感 Claude 3…

Python基础教程(二十九):operator模块

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

Upload-Labs-Linux1 使用 一句话木马

解题步骤&#xff1a; 1.新建一个php文件&#xff0c;编写内容&#xff1a; <?php eval($_REQUEST[123]) ?> 2.将编写好的php文件上传&#xff0c;但是发现被阻止&#xff0c;网站只能上传图片文件。 3.解决方法&#xff1a; 将php文件改为图片文件&#xff08;例…

【代码随想录算法训练Day42】LeetCode 1049.最后一块石头的重量II、LeetCode 494.目标和、LeetCode 474.一和零

Day42 动态规划第四天 LeetCode 1049.最后一块石头的重量II dp数组的含义&#xff1a;容量为j的背包能装的物品的最大价值&#xff08;最大重量&#xff09;为dp[j]。 递推公式&#xff1a;dp[j]max(dp[j],dp[j-weight[i]]value[i]) 初始化&#xff1a;dp[0]0,dp[j]0。 遍历顺…

白嫖游戏指南,Epic喜加二:《Freshly Frosted》《Rumble Club》

前言 Epic喜加二&#xff1a;《Freshly Frosted》《Rumble Club》《Freshly Frosted》简介&#xff1a; 《Rumble Club》简介&#xff1a; 前言 接下来有时间会分享一些游戏相关可以白嫖的资源&#xff0c;包括游戏本体、游戏素材资源等等。 有需要的小伙伴可以关注这个专栏&…

IP SSL证书使用率大幅度提升

IP SSL证书的使用人数在增长&#xff0c;这一趋势背后有几个推动因素&#xff1a; 1.网络安全意识提升&#xff1a;随着网络安全事件频发&#xff0c;用户和企业对数据保护的重视程度日益增加。IP SSL证书能为基于IP地址直接访问的网站或服务提供加密&#xff0c;有助于防止数据…

等保测评练习卷5

等级保护初级测评师试题5 姓名&#xff1a; 成绩&#xff1a; 判断题&#xff08;10110分&#xff09; 启用安全增强型Linux的permissive模式&#xff0c;违反SElinux规则的行为只会被记录到日志中 (T ) p106 除管理员用户外&…