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

java 二分查找 方法 详解

Java中的二分查找是一种常用的搜索算法,用于在**有序集合(如数组或列表)**中快速查找目标值或其相关位置。Java提供了多种内置方法,同时也支持自定义实现。以下将详细解析二分查找的方法、实现及其应用场景。


1. Java中的二分查找方法

(1) Arrays.binarySearch (数组二分查找)

Arrays.binarySearch 是 Java 提供的内置二分查找方法,专用于 数组

方法签名
// 对基础类型数组进行二分查找
public static int binarySearch(int[] a, int key)// 对引用类型数组进行二分查找
public static <T> int binarySearch(T[] a, T key, Comparator<? super T> c)
工作原理
  1. 适用于 升序排序 的数组。
  2. 返回结果:
    • 如果找到目标值,返回目标值的索引。
    • 如果未找到目标值,返回 -(插入点 + 1),其中插入点是目标值应该插入的位置。
示例代码
import java.util.Arrays;public class BinarySearchExample {public static void main(String[] args) {// 基础类型数组int[] arr = {1, 3, 5, 7, 9};int target = 5;// 二分查找int result = Arrays.binarySearch(arr, target);System.out.println("目标值 " + target + " 的索引是:" + result);// 未找到目标值的情况target = 6;result = Arrays.binarySearch(arr, target);System.out.println("未找到目标值,返回值:" + result);}
}
输出结果
目标值 5 的索引是:2
未找到目标值,返回值:-4
注意事项
  • 输入数组必须是升序排序。如果未排序,结果是未定义的。
  • 对于基础类型数组,如 int[]double[] 等,使用默认的自然排序。
  • 对引用类型数组(如 String[]),可以自定义比较器。

(2) Collections.binarySearch (集合二分查找)

Collections.binarySearch 是 Java 提供的另一个二分查找方法,专用于 列表

方法签名
// 默认排序
public static <T extends Comparable<? super T>> int binarySearch(List<? extends T> list, T key)// 自定义排序
public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
工作原理
  1. 适用于 升序排序 的列表(List 接口实现类,如 ArrayListLinkedList)。
  2. 返回结果与 Arrays.binarySearch 相同:
    • 找到目标值,返回其索引。
    • 未找到目标值,返回 -(插入点 + 1)
示例代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;public class BinarySearchListExample {public static void main(String[] args) {// 创建升序排序的列表List<Integer> list = new ArrayList<>();list.add(1);list.add(3);list.add(5);list.add(7);list.add(9);// 默认排序二分查找int target = 5;int result = Collections.binarySearch(list, target);System.out.println("目标值 " + target + " 的索引是:" + result);// 自定义排序二分查找target = 6;result = Collections.binarySearch(list, target);System.out.println("未找到目标值,返回值:" + result);}
}
输出结果
目标值 5 的索引是:2
未找到目标值,返回值:-4
注意事项
  • 输入列表必须是升序排序。
  • 如果需要自定义排序,则必须提供一个 Comparator

2. 自定义实现二分查找

除了使用内置方法,还可以根据需求自定义二分查找逻辑。以下是常见实现:


(1) 迭代实现

代码实现
public class CustomBinarySearch {public static int binarySearch(int[] arr, int target) {int left = 0, 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, 3, 5, 7, 9};int target = 7;int result = binarySearch(arr, target);if (result != -1) {System.out.println("目标值 " + target + " 的索引是:" + result);} else {System.out.println("目标值 " + target + " 不在数组中。");}}
}
输出结果
目标值 7 的索引是:3

(2) 递归实现

代码实现
public class RecursiveBinarySearch {public static int binarySearch(int[] arr, int target, int left, int right) {if (left > right) {return -1; // 搜索结束}int mid = left + (right - left) / 2;if (arr[mid] == target) {return mid; // 找到目标值} else if (arr[mid] < target) {return binarySearch(arr, target, mid + 1, right); // 搜索右半部分} else {return binarySearch(arr, target, left, mid - 1); // 搜索左半部分}}public static void main(String[] args) {int[] arr = {1, 3, 5, 7, 9};int target = 9;int result = binarySearch(arr, target, 0, arr.length - 1);if (result != -1) {System.out.println("目标值 " + target + " 的索引是:" + result);} else {System.out.println("目标值 " + target + " 不在数组中。");}}
}
输出结果
目标值 9 的索引是:4

3. 查找变种实现

(1) 查找插入位置

当目标值不存在时,返回它应该插入的位置。

代码实现
public class FindInsertPosition {public static int searchInsert(int[] arr, int target) {int left = 0, right = arr.length;while (left < right) {int mid = left + (right - left) / 2;if (arr[mid] < target) {left = mid + 1;} else {right = mid; // 收缩右边界}}return left; // 插入位置}public static void main(String[] args) {int[] arr = {1, 3, 5, 6};int target = 4;System.out.println("目标值的插入位置:" + searchInsert(arr, target));}
}
输出结果
目标值的插入位置:2

(2) 查找重复元素的第一个或最后一个位置

代码实现
public class FindFirstAndLast {public static int findFirst(int[] arr, int target) {int left = 0, right = arr.length - 1, 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 findLast(int[] arr, int target) {int left = 0, right = arr.length - 1, 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;}public static void main(String[] args) {int[] arr = {1, 2, 2, 2, 3, 4, 5};int target = 2;int first = findFirst(arr, target);int last = findLast(arr, target);System.out.println("目标值 " + target + " 的第一个位置:" + first);System.out.println("目标值 " + target + " 的最后一个位置:" + last);}
}
输出结果
目标值 2 的第一个位置:1
目标值 2 的最后一个位置:3

4. 二分查找的时间与空间复杂度

时间复杂度

  • 时间复杂度:O(log n)
    每次迭代将搜索范围缩小一半。

空间复杂度

  • 空间复杂度:O(1) (迭代实现)
  • 空间复杂度:O(log n) (递归实现,受限于递归栈的深度)

5. 二分查找的应用场景

典型场景

  1. 在有序数组中快速查找目标值。
  2. 寻找插入位置(如在动态更新数组中保持排序)。
  3. 解决查找范围问题(如查找第一个或最后一个满足条件的元素)。
  4. 算法优化
    • 二分查找可以用于复杂问题的优化(如搜索空间的最优值)。
    • 平方根计算分配资源等问题。

注意事项

  • 数组或列表必须是 有序的,否则二分查找的结果是未定义的。
  • 对于查找第一个或最后一个满足条件的元素,需要结合搜索边界调整算法。

6. 总结

方法场景时间复杂度空间复杂度
Arrays.binarySearch数组查找O(log n)O(1)
Collections.binarySearch列表查找O(log n)O(1)
自定义迭代实现数组或列表查找O(log n)O(1)
自定义递归实现数组或列表查找O(log n)O(log n)
查找插入位置插入排序时保持有序O(log n)O(1)
查找重复元素的范围统计元素出现的第一个或最后一个位置O(log n)O(1)

二分查找是高效的搜索算法,适合解决各种有序集合中的搜索问题。在使用前务必确保输入数据是有序的,并根据需求选择合适的实现方法。


http://www.mrgr.cn/news/77964.html

相关文章:

  • C语言中的结构体,指针,联合体的使用
  • CMS收集器和G1收集器
  • 洛谷P8834
  • QT基础教程(QT网络编程)
  • 数据结构与算法——1120——时间空间效率问题求边界值
  • Keil基于ARM Compiler 5的工程迁移为ARM Compiler 6的工程
  • 虚幻引擎---术语篇
  • 4.SynchronousMethodHandler
  • Spring Boot 动态数据源切换
  • 十一、排他思想、window、延时定时器、间歇函数、时间戳、location、navigator、history、本地存储localStorage
  • C++设计模式-享元模式
  • 安装 Docker(使用国内源)
  • 从0开始学PHP面向对象内容之常用设计模式(适配器,桥接,装饰器)
  • 大模型系列11-ray
  • 疑难Tips:NextCloud域名访问登录时卡住,显示违反内容安全策略
  • k8s网络服务
  • C#设计模式——抽象工厂模式(重点)
  • Vue3响应式原理
  • Springboot项目搭建-Maven打包编译
  • 演示如何使用 `nn.CrossEntropyLoss` 来计算交叉熵损失,计算损失值的演示代码,和讲解 ,CrossEntropyLoss 损失数值等于零的原因
  • hugo文章支持数学公式
  • oracle 12c查看执行过的sql及当前正在执行的sql
  • 【计算机网络】多路转接之select
  • 新华三嵌入式面试题及参考答案
  • 海信Java后端开发面试题及参考答案
  • 第三十九篇 ShuffleNet V1、V2模型解析