Java 实现二分查找:[通俗易懂的算法系列之四]
引言
大家好!欢迎来到我的算法学习系列的第四篇。排序的主要目的之一,就是为了能够更快速地查找数据。
今天,我们将学习一种必须在有序数据上才能使用的、非常高效的查找算法——二分查找 (Binary Search),也叫折半查找。想象一下在一部厚厚的、按字母顺序排列的字典里查找一个单词,你通常不会从第一页开始逐页翻看,而是会直接翻到大概中间的位置,然后根据当前页的字母与目标单词的比较,决定向前翻还是向后翻,每次都将查找范围缩小一半。这就是二分查找的核心思想。
什么是二分查找?
二分查找是一种在有序数组中查找特定元素的算法。它的基本思路是:
- 确定查找范围: 从整个有序数组开始。
- 找到中间元素: 计算查找范围的中间位置索引。
- 比较:
- 如果中间元素等于目标值,查找成功。
- 如果中间元素小于目标值,说明目标值(如果存在)一定在中间元素的右边,因此,缩小查找范围到右半部分。
- 如果中间元素大于目标值,说明目标值(如果存在)一定在中间元素的左边,因此,缩小查找范围到左半部分。
- 重复: 在新的、缩小的查找范围内重复步骤 2 和 3,直到找到目标值或查找范围为空(表示目标值不存在)。
关键前提: 二分查找必须作用于一个已经排好序的数组(通常是升序)。如果数组无序,二分查找将无法保证正确性。
算法步骤详解 (以升序数组为例)
假设我们要在有序数组 arr = [1, 5, 8, 11, 19, 22, 31, 35, 40, 45, 48, 49, 50]
中查找 target = 48
。
-
初始化:
- 左边界
left = 0
- 右边界
right = arr.length - 1 = 12
- 查找范围
[0, 12]
- 左边界
-
第 1 次迭代:
mid = left + (right - left) / 2 = 0 + (12 - 0) / 2 = 6
arr[mid] = arr[6] = 31
arr[mid] (31) < target (48)
-> 目标在右半部分。- 更新
left = mid + 1 = 6 + 1 = 7
- 新查找范围
[7, 12]
-
第 2 次迭代:
mid = left + (right - left) / 2 = 7 + (12 - 7) / 2 = 7 + 5 / 2 = 7 + 2 = 9
(整数除法)arr[mid] = arr[9] = 45
arr[mid] (45) < target (48)
-> 目标在右半部分。- 更新
left = mid + 1 = 9 + 1 = 10
- 新查找范围
[10, 12]
-
第 3 次迭代:
mid = left + (right - left) / 2 = 10 + (12 - 10) / 2 = 10 + 2 / 2 = 10 + 1 = 11
arr[mid] = arr[11] = 49
arr[mid] (49) > target (48)
-> 目标在左半部分。- 更新
right = mid - 1 = 11 - 1 = 10
- 新查找范围
[10, 10]
-
第 4 次迭代:
mid = left + (right - left) / 2 = 10 + (10 - 10) / 2 = 10 + 0 / 2 = 10
arr[mid] = arr[10] = 48
arr[mid] (48) == target (48)
-> 找到目标!- 返回索引
mid = 10
。
如果查找 target = 6
(不存在):
- …经过几次迭代后…
- 假设某次迭代后
left = 1
,right = 0
。 - 此时
left > right
,循环条件while (left <= right)
不满足,循环结束。 - 表示未找到目标值。
Java 代码实现
下面是二分查找的 Java 代码实现(迭代版本),与你提供的一致,并包含了一些好的实践:
import java.util.Arrays; // 如果你想方便地打印数组,可以导入 Arrays 类
public class BinarySearch {
// 类名建议大写开头