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

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.mrgr.cn/news/50975.html

相关文章:

  • 【分布式微服务云原生】《探秘分布式系统基石:CAP、BASE 理论与 Soft 状态》
  • 腾讯云视立方·音视频通话 SDK 个人信息保护规则
  • Leetcode 第 141 场双周赛题解
  • 深入理解C++ STL中的 vector
  • (亲测可行)windows安装msys2配置c++opencv
  • Excel使用技巧:筛选2组数据;条件格式突出显示数据
  • Zsh 安装与配置
  • 小程序开发设计-模板与配置:WXML模板语法⑨
  • win11安装不了msi文件解决办法
  • 利士策分享,美国“假旗”行动,是否成为了网络空间的阴霾?
  • 机器学习:opencv--人脸检测以及微笑检测
  • HCIP-HarmonyOS Application Developer 习题(十)
  • Python 工具库每日推荐 【sqlparse】
  • leetcode128最长连续序列 golang版
  • mysql 实用命令
  • Rust默认使用UTF-8编码来解析源代码文件。如果在代码中包含无法用UTF-8编码表示的字符,编译器会报错!
  • 人类与人工智能的和谐关系
  • js 实现斐波那契数列
  • Java基础 03
  • 2024-10-15 学习人工智能的Day7