Java 二分查找算法详解及通用实现模板案例示范

1. 引言

二分查找(Binary Search)是一种常见的搜索算法,专门用于在有序数组或列表中查找元素的位置。它通过每次将搜索空间缩小一半,从而极大地提高了查找效率。相比于线性查找算法,二分查找的时间复杂度为 O(log n),在处理大规模数据时非常高效。

2. 二分查找算法的基本原理

二分查找要求搜索目标的数据集合是有序的。通过将数组从中间位置一分为二,逐步缩小搜索范围,最终找到目标元素或确定目标元素不存在。算法的关键在于,每次比较时,将要查找的元素与中间元素进行比较,并据此决定继续向左半部分或右半部分搜索。

3. 二分查找适用问题类型

二分查找算法适用于以下类型问题:

  1. 在有序数组中查找元素的索引
  2. 寻找有序数组中满足条件的边界值,如第一个大于等于目标值的元素或最后一个小于等于目标值的元素。
  3. 确定某一条件下的最优解,例如在单调递增或递减函数中查找某一阈值。

4. 二分查找通用实现模板

4.1 标准二分查找模板

首先,我们来看最基础的二分查找算法实现。这个模板适用于在有序数组中查找某个目标值,并返回其索引。

public class BinarySearch {/*** 标准二分查找,查找目标值的索引* @param arr 已排序的数组* @param target 要查找的目标值* @return 目标值的索引,如果不存在则返回 -1*/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[] sortedArray = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19};int target = 7;int result = binarySearch(sortedArray, target);if (result != -1) {System.out.println("目标值 " + target + " 在索引 " + result);} else {System.out.println("目标值 " + target + " 不存在于数组中");}}
}

解释

  • leftright 分别指向数组的左右边界。
  • 每次通过 mid = left + (right - left) / 2 计算中间索引。
  • 如果中间值 arr[mid] 恰好等于目标值,则返回 mid 作为目标值的索引。
  • 如果中间值小于目标值,表示目标值在右侧,则更新左边界 left = mid + 1
  • 如果中间值大于目标值,表示目标值在左侧,则更新右边界 right = mid - 1
4.2 二分查找的变体模板

除了标准的二分查找,还有一些变体问题常常需要我们在查找过程中找到特定的边界值。

查找第一个大于等于目标值的元素
public static int findFirstGreaterOrEqual(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;  // 记录当前 mid 作为可能的结果right = mid - 1;  // 继续在左半部分查找} else {left = mid + 1;  // 在右半部分查找}}return result;
}
查找最后一个小于等于目标值的元素
public static int findLastLessOrEqual(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;  // 记录当前 mid 作为可能的结果left = mid + 1;  // 继续在右半部分查找} else {right = mid - 1;  // 在左半部分查找}}return result;
}

5. 二分查找的时序图

为了更好地理解二分查找的执行过程,我们可以通过时序图展示二分查找的各个步骤。时序图展示了从开始到查找到目标值(或确认目标值不存在)的过程。

时序图

在这里插入图片描述

6. 案例分析:电商系统中的二分查找

在电商系统中,二分查找可以被应用在很多场景中,例如:

  1. 商品价格查询:在大量已排序的商品价格列表中快速找到某一商品价格,或者确定某个商品价格在列表中的位置。
  2. 库存检查:通过二分查找,可以快速确定某一商品的库存量,或者查找某类商品的库存是否充足。
  3. 订单历史记录查询:在按时间排序的订单历史记录中,快速查找某一时间段的订单。
案例 1:价格查询

假设电商系统有一份已排序的商品价格列表,用户需要查询某个商品的价格是否存在,或者找到第一个高于某个价格的商品。

public class PriceQuery {public static int findFirstPriceGreaterOrEqual(int[] prices, int targetPrice) {int left = 0;int right = prices.length - 1;int result = -1;while (left <= right) {int mid = left + (right - left) / 2;if (prices[mid] >= targetPrice) {result = mid;right = mid - 1;} else {left = mid + 1;}}return result;}public static void main(String[] args) {int[] prices = {100, 200, 300, 400, 500, 600, 700};int targetPrice = 350;int index = findFirstPriceGreaterOrEqual(prices, targetPrice);if (index != -1) {System.out.println("第一个大于等于 " + targetPrice + " 的价格是: " + prices[index]);} else {System.out.println("没有找到大于等于 " + targetPrice + " 的商品价格");}}
}
案例 2:订单历史记录查询

在按日期排序的订单记录中,用户希望快速找到某一天的订单记录,或者查找最近的一笔订单。

public class OrderQuery {public static int findClosestOrder(int[] orders, int targetDate) {int left = 0;int right = orders.length - 1;int closest = -1;while (left <= right) {int mid = left + (right - left) / 2;if (orders[mid] == targetDate) {return mid;  // 找到精确匹配的订单} else if (orders[mid] < targetDate) {closest = mid;  // 记录当前最近的订单left = mid + 1;} else {right = mid - 1;}}return closest;  // 返回最近的订单}public static void main(String[] args) {int[] orderDates = {20220101, 20220201, 20220301, 20220401, 20220501};int targetDate = 20220315;int index = findClosestOrder(orderDates, targetDate);if (index != -1) {System.out.println("最近的订单日期是: " + orderDates[index]);} else {System.out.println("没有找到合适的订单");}}
}

7. 二分查找的常见问题

虽然二分查找看似简单,但在实现过程中容易遇到一些问题。

  1. 越界问题:由于计算中间索引时使用 (left + right) / 2,当 leftright 很大时,可能会导致整型溢出。为此,推荐使用 left + (right - left) / 2 计算中间索引。
  2. 终止条件不清晰:二分查找的循环条件应为 left <= right,而不是 left < right,否则可能会漏掉最后一个可能的索引位置。

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

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

相关文章

Arthas常用的命令(三)--monitor、jad 、stack

monitor&#xff1a;监控方法的执行情况 监控指定类中方法的执行情况 用来监视一个时间段中指定方法的执行次数&#xff0c;成功次数&#xff0c;失败次数&#xff0c;耗时等这些信息 参数说明 方法拥有一个命名参数 [c:]&#xff0c;意思是统计周期&#xff08;cycle of ou…

linux线程 | 同步与互斥(上)

前言&#xff1a;本节内容主要是线程的同步与互斥。 本篇文章的主要内容都在讲解互斥的相关以及周边的知识。大体的讲解思路是通过数据不一致问题引出锁。 然后谈锁的使用以及申请锁释放锁的原子性问题。 那么&#xff0c; 废话不多说&#xff0c; 现在开始我们的学习吧&#x…

软件测试工程师面试整理 —— 操作系统与网络基础!

在软件测试中&#xff0c;了解操作系统和网络基础知识对于有效地进行测试工作至关重要。无论是在配置测试环境、调试网络问题&#xff0c;还是在进行性能测试和安全测试时&#xff0c;这些知识都是不可或缺的。 1. 操作系统基础 操作系统&#xff08;Operating System, OS&am…

OgreNext高级材质中增加线宽,点大小,虚线模式绘制支持

修改Ogre高级材质系统&#xff0c;增加线宽&#xff0c;点大小&#xff0c;虚线模式&#xff0c;虚线参数的支持,效果如下&#xff1a; 需要修改的代码文件如下&#xff1a; 修改如下 代码文本&#xff1a; //范围[0.2 - 51] 0.2 * [0,255];Ogre::uint8 mLineWidth;//范围[…

【数据结构】:破译排序算法--数字世界的秩序密码(二)

文章目录 前言一.比较排序算法1.Bubble Sort冒泡排序1.1.冒泡排序原理1.2.冒泡排序过程1.3.代码实现1.4.复杂度和稳定性 2.Quick Sort快速排序2.1递归快速排序2.1.1.递归快速排序原理2.1.2.递归快速排序过程2.1.3.代码实现 2.2.非递归快速排序2.2.1.非递归快速排序原理2.2.2.非…

MATLAB智能优化算法-学习笔记(5)——蚁群算法求解容量受限的车辆路径问题

蚁群算法在求解容量受限的车辆路径问题(Capacitated Vehicle Routing Problem, CVRP)中具有广泛应用。这类问题属于组合优化问题,涉及将若干辆具有容量限制的车辆,从配送中心出发为多个客户点提供服务,要求每辆车满足各客户的需求且总运载量不超过车辆容量,最终需要找到一…

python深浅拷贝,可变变量与不可变变量

赋值 在 python 中&#xff0c;赋值是将一个值或对象分配给一个变量的过程。赋值操作符是 &#xff0c;用于将右侧的值或对象赋给左侧的变量。 赋值&#xff1a;l2的值会随着原对象l1的值一同改变 l1 [1, 2, 3, 4] print(l1:, l1) l2 l1 print(l2:, l2) 给li列表新增元素 …

检测头篇 | 手把手教你如何去更换YOLOv8的检测头为ASFF_Detect

前言:Hello大家好,我是小哥谈。自适应空间特征融合(ASFF)的主要原理旨在解决单次检测器中不同尺度特征的不一致性问题。具体来说,ASFF通过动态调整来自不同尺度特征金字塔层的特征贡献,确保每个检测对象的特征表示是一致且最优的。本文所做出的改进是将YOLOv8的检测头更换…

使用 Spring 框架构建 MVC 应用程序:初学者教程

Spring Framework 是一个功能强大、功能丰富且设计精良的 Java 平台框架。它提供了一系列编程和配置模型&#xff0c;旨在简化和精简 Java 中健壮且可测试的应用程序的开发过程。 人们常说 Java 太复杂了&#xff0c;构建简单的应用程序需要很长时间。尽管如此&#xff0c;Jav…

论文翻译 | OpenICL: An Open-Source Framework for In-context Learning

摘要 近年来&#xff0c;上下文学习&#xff08;In-context Learning&#xff0c;ICL&#xff09;越来越受到关注&#xff0c;并已成为大型语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;评估的新范式。与传统微调方法不同&#xff0c;ICL无需更新任何参…

龙信科技:引领电子物证技术,助力司法公正

文章关键词&#xff1a;电子数据取证、电子物证、手机取证、计算机取证、云取证、介质取证 在信息技术飞速发展的今天&#xff0c;电子物证在司法领域扮演着越来越重要的角色。苏州龙信信息科技有限公司&#xff08;以下简称“龙信科技”&#xff09;作为电子数据取证领域的先…

bat(批处理脚本学习)

输出banner echo off echo () echo JL echo ^|^| echo LJ echo _,--"""""""---. echo , …

从零实现高并发内存池

目录 1. 项目介绍1.1 这个项目具体功能是什么&#xff1f;1.2 本项目的知识储备 2. 什么是内存池2.1 池化技术2.2 内存池主要解决的问题2.3 malloc 3. 定长内存池设计4. 高并发内存池整体框架设计4.1 Thread Cache的设计思路4.2 Central Cache的设计思路4.3 Page Cache的设计思…

【C语言】分支结构switch

switch分支语句 多适用于明确表达式结果的情况&#xff0c;多个分支&#xff0c;用if过于繁琐。 case后跟具体的表达式值&#xff0c;break&#xff1b;跳出分支语句。 #include <stdio.h> #include <math.h> /* 功能&#xff1a;选择结构&#xff08;switch&…

Qt初识_项目文件解析

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 Qt初识_项目文件解析 收录于专栏【Qt开发】 本专栏旨在分享学习Qt的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. pro文件解析 2.…

java异步多线程Async学习记录

java异步多线程Async学习记录 第1步:声明线程池AsyncConfiguration import org.springframework.context.annotation.Bean; import org.springframework

vue+element的confirm提示消息文字变色和换行

效果: 思路: 可以考虑采用模板字符串的思路实现 代码: this.confirm(您确定要<b style"Color: red">${text}</b>的数据项&#xff1f;<br/>单位名称: ${row.companyName} <br/>属性: ${row.attributeName}).then(() > {console.log(确定…

SCM供应商管理怎么做?

在企业的供应链管理中&#xff0c;供应商管理是至关重要的一环。然而&#xff0c;传统的供应商管理方式常常面临诸多痛点&#xff0c;导致管理效率低下、成本增加、风险增大。不注重供应商管理的企业&#xff0c;常常会面临以下问题&#xff1a; 供应商档案管理难&#xff1a;…

Redis 五种数据类型的操作命令

一、五种数据类型的介绍 五种数据类型如图所示&#xff1a; Redis 是一个开源的键值存储系统&#xff0c;它支持多种数据结构&#xff0c;每种数据结构都有其特定的用例和底层实现。以下是 Redis 的五种主要数据类型&#xff0c;以及它们适合存储的数据类型和底层实现&#xf…

健康生活的重要性

在当今快节奏的生活中&#xff0c;养生保健已成为人们日益关注的话题&#xff0c;而健身作为其中的重要一环&#xff0c;更是被赋予了前所未有的重视。谈及养生保健与健身&#xff0c;我们不得不深入思考&#xff1a;如何在繁忙的日常中&#xff0c;找到那条通往健康与活力的道…