数据结构第24节 二分查找

二分查找(Binary Search),也被称为折半查找,是一种在有序数组中查找特定元素的高效算法。它的基本思想是将查找区间分为两部分,通过比较中间元素与目标值来决定下一步是在哪一半继续查找。

二分查找的步骤:

  1. 初始化:定义两个指针 leftright 分别指向数组的起始位置和结束位置。
  2. 循环条件:当 left <= right 时,继续执行以下步骤。
  3. 计算中间位置mid = (left + right) / 2。为了避免整型溢出,通常使用 mid = left + (right - left) / 2 或者 mid = (left + right) >>> 1
  4. 比较:比较数组中间位置的元素 arr[mid] 与目标值 target
    • 如果 arr[mid] == target,则找到了目标值,返回 mid
    • 如果 arr[mid] < target,则更新 left = mid + 1,因为目标值可能位于右侧子数组。
    • 如果 arr[mid] > target,则更新 right = mid - 1,因为目标值可能位于左侧子数组。
  5. 重复步骤3和4,直到找到目标值或者 left > right
  6. 未找到:如果退出循环是因为 left > right,说明数组中不存在目标值,返回 -1 或者其他表示未找到的值。

Java 实现:

下面是一个简单的二分查找的Java实现示例:

public class BinarySearch {public static int binarySearch(int[] arr, int target) {int left = 0;int right = arr.length - 1;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 - 1;}}// 目标值不在数组中return -1;}public static void main(String[] args) {int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};int target = 4;int result = binarySearch(arr, target);if (result != -1) {System.out.println("Element found at index: " + result);} else {System.out.println("Element not found in the array");}}
}

时间复杂度:

二分查找的时间复杂度为 O(log n),其中 n 是数组的长度。这是因为每次比较都会将搜索空间减半,直到找到目标值或搜索空间为空。

注意事项:

  • 数组必须是有序的。
  • 二分查找仅适用于静态数组,对于动态变化的数据结构,如频繁插入删除的列表,可能不是最佳选择。
  • 在处理边界条件和溢出时要小心,以避免潜在的错误。

在Java中优化二分搜索代码主要可以从以下几个方面进行:

  1. 防止整数溢出:在计算中间位置时,使用 (left + right) / 2 可能会遇到整数溢出的问题,尤其是在 leftright 均为较大的正整数时。解决这个问题的一种方法是使用 (left + right) >>> 1left + (right - left) / 2 来计算中间位置。

  2. 减少不必要的计算:在循环内部,可以避免重复计算已经确定的值,比如 mid 的计算只应在每次循环开始时进行一次。

  3. 简化循环条件:确保循环终止条件是最简洁有效的形式。

  4. 处理边界情况:确保你的代码能够正确处理边界情况,比如空数组、单元素数组等。

下面是一个优化过的二分查找Java代码示例:

public class BinarySearch {public static int binarySearch(int[] arr, int target) {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;}}// 目标值不在数组中return -1;}public static void main(String[] args) {int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};int target = 4;int result = binarySearch(arr, target);if (result != -1) {System.out.println("Element found at index: " + result);} else {System.out.println("Element not found in the array");}}
}

在这个版本中,我们使用了 left + (right - left) / 2 来避免整数溢出问题,并且循环条件和计算逻辑已经尽可能地简化和标准化。此外,我们还应该考虑是否需要处理数组为空的情况,这可以通过在循环之前添加一个检查来完成:

if (arr == null || arr.length == 0) {return -1;
}

最后,根据实际应用的需求,你可能还需要考虑以下几点:

  • 如果数组中有多个相同的元素,你可能需要修改算法来找到第一个或最后一个匹配项。
  • 如果你需要在没有找到目标值的情况下插入它,可以利用返回值来确定插入位置。
  • 对于非常大的数据集,考虑使用并行或分段的二分搜索算法,以提高性能。

为了进一步完善二分查找的代码,我们可以考虑以下几点:

  1. 处理特殊情况:例如,如果数组为空或null,应立即返回适当的结果。
  2. 寻找目标值的第一个或最后一个出现的位置:这对于有重复元素的数组特别有用。
  3. 查找目标值的插入位置:当目标值不存在于数组中时,返回其应该被插入的位置。

接下来,我们将分别针对这些场景提供代码示例。

1. 处理特殊情况

public static int binarySearch(int[] arr, int target) {if (arr == null || arr.length == 0) {return -1; // 或者返回一个特殊值,如 arr.length 表示目标值应插入数组末尾}int left = 0;int right = arr.length - 1;// ... 其他代码 ...
}

2. 查找目标值的第一个或最后一个出现的位置

假设数组可能包含重复的目标值,我们希望找到它的第一次出现或最后一次出现的位置。

第一次出现的位置
public static int findFirstOccurrence(int[] arr, int target) {int left = 0;int right = arr.length - 1;int result = -1;while (left <= right) {int mid = left + (right - left) / 2;if (arr[mid] == target) {result = mid;right = mid - 1; // 继续在左边查找} else if (arr[mid] < target) {left = mid + 1;} else {right = mid - 1;}}return result;
}
最后一次出现的位置
public static int findLastOccurrence(int[] arr, int target) {int left = 0;int right = arr.length - 1;int result = -1;while (left <= right) {int mid = left + (right - left) / 2;if (arr[mid] == target) {result = mid;left = mid + 1; // 继续在右边查找} else if (arr[mid] < target) {left = mid + 1;} else {right = mid - 1;}}return result;
}

3. 查找目标值的插入位置

当目标值不存在于数组中时,返回其应该被插入的位置。

public static int findInsertPosition(int[] arr, int target) {int left = 0;int right = arr.length;while (left < right) {int mid = left + (right - left) / 2;if (arr[mid] < target) {left = mid + 1;} else {right = mid;}}return left; // 返回插入位置
}

这些函数可以根据具体的应用场景进行调用,以满足不同的需求。

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

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

相关文章

​Chrome 插件: GoFullPage 一键搞定全网页截图

在互联网时代&#xff0c;网页截图已成为我们日常工作和生活中不可或缺的部分。无论是保存重要信息、制作教程&#xff0c;还是分享有趣的内容&#xff0c;截图功能都显得尤为重要。然而&#xff0c;常规的截图工具往往只能截取当前屏幕的内容&#xff0c;对于长网页则显得力不…

做个简单的知识付费网站需要什么方式

网站是线上承载信息宣传的主要工具之一&#xff0c;也是企业公司发展的重要工具之一&#xff0c;除了固定信息呈现外&#xff0c;还有不少商家具备各种方式的干货输出能力&#xff0c;或者想以内容售卖获得一定营收。 如教培机构、自媒体、网校、知识生产者、领域达人等都具备…

构建艺术:在Gradle中定制化输出的精粹

构建艺术&#xff1a;在Gradle中定制化输出的精粹 引言 Gradle是一个高度可配置的构建自动化工具&#xff0c;它广泛应用于现代软件开发中。在构建过程中&#xff0c;合理地配置构建输出对于项目的构建效率、部署和维护至关重要。本文将深入探讨如何在Gradle中配置构建输出&a…

【unity笔记】九、Unity添加串口通信

unity仿真使用虚拟串口调试。下面为简单流程。 常用串口调试软件在这里下载。 1.虚拟串口 添加虚拟串口&#xff0c;这里使用com1 com2 2. 串口调试 在这里为虚拟串口发送消息。 3. unity配置 3.1 设置 在文件->生成设置->玩家设置->玩家->其他设置 中找到…

【机器学习】逻辑回归的原理、应用与扩展

文章目录 一、逻辑回归概述二、Sigmoid函数与损失函数2.1 Sigmoid函数2.2 损失函数 三、多分类逻辑回归与优化方法3.1 多分类逻辑回归3.2 优化方法 四、特征离散化 一、逻辑回归概述 逻辑回归是一种常用于分类问题的算法。大家熟悉的线性回归一般形式为 Y a X b \mathbf{Y}…

初学SpringMVC之 JSON 篇

JSON&#xff08;JavaScript Object Notation&#xff0c;JS 对象标记&#xff09;是一种轻量级的数据交换格式 采用完全独立于编程语言的文本格式来存储和表示数据 JSON 键值对是用来保存 JavaScript 对象的一种方式 比如&#xff1a;{"name": "张三"}…

「Pytorch」roLabelImg 图像异常旋转 bug

在进行Yolo-obb 模型训练的时候需要标注旋转框&#xff0c;roLabelImg 是比较推荐的一款旋转框标注工具&#xff0c;既可以标注正常的矩形框&#xff0c;还可以标注旋转框 roLabelImg Github 地址&#xff1a;https://github.com/HumanSignal/labelImg 但是在使用过程中遇到了…

SpringCloud学习

认识微服务 1.单体架构&#xff1a;将业务的所有功能集中在一个项目中开发&#xff0c;打成一个包部署 优点&#xff1a;架构简单 部署成本低 缺点&#xff1a;耦合度高 2.分布式架构&#xff1a;根据业务功能对系统进行拆分&#xff0c;每个业务模块作为独立项目开发&…

k8s record 20240710 监控

不是adaptor 是opetator 案例 监控有了&#xff0c;日志搜集呢&#xff1f; 一、kubelet 的小弟 kubelet — 负责维护容器的生命周期&#xff0c;节点和集群其他部分通信 cAdvisor 集成在 Kubernetes 的 kubelet 中&#xff0c;能够自动发现和监控集群中所有的容器。dockers…

005-基于Sklearn的机器学习入门:逻辑回归

本节将介绍机器学习中一种简单而又经典的分类算法&#xff1a;逻辑回归。 机器学习&#xff1a;逻辑回归原理_机器学习做回归的原理-CSDN博客 机器学习实战&#xff1a;Python基于Logistic逻辑回归进行分类预测&#xff08;一&#xff09;_lr逻辑回归 python-CSDN博客

创业者一定要做好时间管理

2024.7.5 最近接了两个项目&#xff0c;给我拖入了战争泥潭&#xff0c;耗费了大量的时间和精力。再加上今天不知道咋回事&#xff0c;有好多客户来咨询&#xff0c;就搞得人很疲惫&#xff0c;脑袋快炸了一样&#xff0c;感觉再这样下去会积怨成疾。现在能深刻的体会到&#x…

YOLOv5白皮书-第Y5周:yolo.py文件解读

本文为365天深度学习训练营 中的学习记录博客 原作者&#xff1a;K同学啊|接辅导、项目定制 本次训练是在前文《YOLOv5白皮书-第Y2周:训练自己的数据集》的基础上进行的。 前言 文件位置:./models/yolo.Py 这个文件是YOLOv5网络模型的搭建文件&#xff0c;如果你想改进YOLOv5&…

抖音短视频矩阵管理系统搭建全攻略:功能详解与实战应用

在短视频时代&#xff0c;抖音已经成为众多企业、网红、个人创作者不可或缺的传播平台。然而&#xff0c;如何高效管理多个抖音账号&#xff0c;实现内容、数据、粉丝的全方位掌控&#xff0c;成为了摆在大家面前的一道难题。本文将为大家深入解析抖音短视频矩阵管理系统的搭建…

Linux内核中的双向链表介绍

参考文章:https://www.cnblogs.com/liangliangge/p/11359196.html 相关结构体和api的介绍 1.1 struct list_head 用来创建双向循环链表的结构 1.2 INIT_LIST_HEAD 双向链表初始化,让一个链表节点首尾相连 1.3 list_add和list_add_tail 给链表增加一个结点 list_add :…

从0开始学习informer

目录 informer特点informer原理attention计算KL散度 backbone网络部分encoder输入输出部分embadding这里就不讲了 和transfomer一样EncoderStack decoder部分接下来就是最关键的结构 关于如何将输入经过注意力得到结果 结束&#xff0c;代码会放到下一篇讲 这里是原理 informer…

基于GIS矿产勘查靶区优选技术

定义&#xff1a; 找矿远景区(ore-finding prospect)&#xff1a; 一般将中小比例尺&#xff08;小于等于1&#xff1a;10万&#xff09;成矿预测所圈定的找矿有利地段&#xff08;preferable ore-finding area&#xff09;成为找矿远景区 找矿靶区&#xff08;ore-finding t…

车流量统计YOLOV8+DEEPSORT

车流量统计&#xff0c;YOLOV8NANODEEPSORT资源-CSDN文库 车流量统计YOLOV8DEEPSORT&#xff0c;目前支持PYTHON,C开发 PYTHON版本&#xff0c;需要YOLOV8&#xff0c;依赖PYTORCH C版本&#xff0c;只需要OPENCV

linux kernel ptr dump

指针如何打印和调试 Plain Pointers %p abcdef12 or 00000000abcdef12 Pointers printed without a specifier extension (i.e unadorned %p) are hashed to prevent leaking information about the kernel memory layout. This has the added benefit of providing a uni…

MYSQL的面试题

目录 一.数据库的约束类型 一.创建数据库、修改数据库名、修改表名&#xff0c;修改列名、修改某个属性的语法 三.索引的类型、优缺点以及使用场景 四.索引的常见的索引数据结构 五.数据库中常用的锁 六.事务的四大特性 七.什么是脏读&#xff1f;幻读&#xff1f;不可重…

技术周总结 2024.07.08~07.14(算法,Python,Java,Scala,PHP)

文章目录 一、07.13 周六1.0&#xff09;算法题&#xff1a;字符串中的单词反转1.1&#xff09; 问题01:可靠性计算中的MTTR MTTF MTBF 分别指什么&#xff1f;他们之间有什么联系&#xff1f;MTTR (Mean Time to Repair)MTTF (Mean Time to Failure)MTBF (Mean Time Between F…