public class BinarySearch {
/**
* 標準二分查找:返回目標值索引,不存在返回 -1
* @param arr 升序陣列
* @param target 目標值
* @return 索引或 -1
*/
public static int binarySearch(int[] arr, int target) {
if (arr == null || arr.length == 0) return -1;
int left = 0;
int right = arr.length - 1; // 閉區間 [left, right]
while (left <= right) { // 區間不為空
int mid = left + ((right - left) >> 1); // 防溢位
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[] data = {1, 3, 5, 7, 9, 11, 13};
System.out.println(binarySearch(data, 7)); // 3
System.out.println(binarySearch(data, 8)); // -1
System.out.println(binarySearch(data, 1)); // 0
System.out.println(binarySearch(data, 13)); // 6
}
}
當陣列含重複元素,標準版返回任意一個匹配索引。實際常需:
>= target 的位置 (lower_bound)<= target 的位置 (upper_bound - 1)
/**
* 查找左邊界:返回第一個 >= target 的索引
* 若所有元素 < target,返回 arr.length (插入位置)
* 等價於 C++ lower_bound
*/
public static int lowerBound(int[] arr, int target) {
int left = 0;
int right = arr.length; // 開區間 [left, right)
while (left < right) {
int mid = left + ((right - left) >> 1);
if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid; // arr[mid] >= target,收縮右邊界
}
}
return left; // left == right
}
/**
* 查找右邊界:返回最後一個 <= target 的索引
* 若所有元素 > target,返回 -1
* 等價於 C++ upper_bound - 1
*/
public static int upperBound(int[] arr, int target) {
int left = 0;
int right = arr.length; // 開區間 [left, right)
while (left < right) {
int mid = left + ((right - left) >> 1);
if (arr[mid] <= target) {
left = mid + 1; // arr[mid] <= target,收縮左邊界
} else {
right = mid;
}
}
return left - 1; // left 是第一個 > target 的位置
}
public static int countOccurrences(int[] arr, int target) {
int left = lowerBound(arr, target);
int right = upperBound(arr, target);
if (left <= right && left < arr.length && arr[left] == target) {
return right - left + 1;
}
return 0;
}
// 範圍計數:統計 [L, R] 區間內元素個數
public static int countRange(int[] arr, int L, int R) {
int left = lowerBound(arr, L);
int right = upperBound(arr, R);
return Math.max(0, right - left + 1);
}
| 方法 | 返回值 | 對應邏輯 | 備註 |
|---|---|---|---|
Arrays.binarySearch(arr, key) | 索引 (找到) / (-(插入點) - 1) (未找到) | 標準查找 | 最常用,返回負數編碼插入位置 |
Collections.binarySearch(list, key) | 同上 | List 版本 | List 必須實作 RandomAccess |
// 解碼 Arrays.binarySearch 返回值
int idx = Arrays.binarySearch(arr, target);
if (idx >= 0) {
System.out.println("找到,索引: " + idx);
} else {
int insertPos = -idx - 1;
System.out.println("未找到,應插入位置: " + insertPos);
}
public static int binarySearchRecursive(int[] arr, int target) {
return binarySearchRecursive(arr, target, 0, arr.length - 1);
}
private static int binarySearchRecursive(int[] arr, int target, int left, int right) {
if (left > right) return -1;
int mid = left + ((right - left) >> 1);
if (arr[mid] == target) return mid;
return arr[mid] < target
? binarySearchRecursive(arr, target, mid + 1, right)
: binarySearchRecursive(arr, target, left, mid - 1);
}
public static > int binarySearch(T[] arr, T target) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = left + ((right - left) >> 1);
int cmp = arr[mid].compareTo(target);
if (cmp == 0) return mid;
if (cmp < 0) left = mid + 1;
else right = mid - 1;
}
return -1;
}
mid = left + ((right - left) >> 1),不可用 (left + right) / 2left <= right,開區間用 left < rightleft = mid + 1 / right = mid - 1 (閉區間);left = mid + 1 / right = mid (開區間)assert isSorted(arr))sqrt(x) 二分整數解WHERE id > last_id ORDER BY id LIMIT n