二分查找的核心逻辑是一样的(每次排除一半),但边界的处理方式(即区间的定义)决定了代码细节的不同
1. 左闭右开区间写法 (binarySearchLCRO)
这种写法将搜索区间定义为 。这意味着 指向的元素包含在搜索范围内,而 指向的元素不包含在范围内(通常是数组长度)。
public class binary_search左闭右开 {
/* 二分查找(左闭右开区间) */
int binarySearchLCRO(int[] nums, int target) {
//初始化左闭右开区间 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
int i = 0 , j = nums.length;
while(i < j){
int m = i + (j - i) / 2;
if (nums[m] < target){
i = m + 1;
} else if (nums[m] > target) {
j = m;
}
else {
return m;
}
}
return -1;
}
}
初始化:
int i = 0, j = nums.length;i指向数组第一个元素。j指向数组最后一个元素的后面(即数组长度)。因为是“右开”,所以 这个下标是取不到的
循环条件:
while(i < j)- 这里使用
<而不是<=。 - 原因: 当 时,区间 是一个空集,没有任何元素,所以循环应该终止。
边界更新:
-
if (nums[m] < target)(目标在右侧):i = m + 1;- 已经被检查过且太小,所以左边界 必须移到 的右边。
-
if (nums[m] > target)(目标在左侧):j = m;- 注意: 这里是
j = m而不是m - 1。 - 原因: 因为区间是“右开”的,即 不包含 。既然 已经确定大于
target,它就不可能是目标值。我们将 设为 ,表示下一次搜索的范围截止到 之前(不含 ),这符合逻辑。
2. 双闭区间写法 (binarySearch)
这是最常见、最容易理解的教科书写法。它将搜索区间定义为 。这意味着 和 指向的元素都包含在搜索范围内
package Algorithm.Search;
import java.util.Arrays;
public class binary_search {
public static void main(String[] args) {
// 二分查找的数组必须是有序的
int[] nums = {1,3,5,6,8,2};
Arrays.sort(nums);
int n = binarySearch(nums,1);
System.out.println(n);
}
/* 二分查找(双闭区间) */
static int binarySearch(int[] nums , int target){
int i = 0 , j = nums.length - 1;
//循环
while(i <= j){
int m = i + (j - i)/2;
if (nums[m] < target){
i = m + 1;
} else if (nums[m] > target) {
j = m - 1;
}
else {
return m;
}
}
//// 未找到目标元素,返回 -1
return -1;
}
}
-
初始化:
int i = 0, j = nums.length - 1;i指向第一个元素。j指向最后一个元素。因为是“双闭”,这两个下标都是有效的。
-
循环条件:
while(i <= j)- 这里必须使用
<=。 - 原因: 当 时,区间 依然包含一个元素(即 ),这个元素还没有被检查过,所以必须进入循环再检查一次。如果用
<,就会漏掉这最后一个元素。
-
边界更新:
-
if (nums[m] < target)(目标在右侧):i = m + 1;- 同上,左边界向右收缩。
-
if (nums[m] > target)(目标在左侧):j = m - 1;- 注意: 这里是
j = m - 1。 - 原因: 因为 是包含在搜索范围内的(闭区间)。既然 已经确认太大,那么 这个位置肯定不是目标。为了下一次搜索不包含它,右边界必须退格到 。
-