跳到主要内容

二分查找

问题

二分查找的模板和常见变体有哪些?

答案

基础模板

标准二分查找
public int binarySearch(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // 防溢出
if (nums[mid] == target) return mid;
else if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}

常见变体

查找第一个等于 target 的位置(左边界)
public int findFirst(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) right = mid - 1;
else left = mid + 1;
}
if (left < nums.length && nums[left] == target) return left;
return -1;
}
查找最后一个等于 target 的位置(右边界)
public int findLast(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) left = mid + 1;
else right = mid - 1;
}
if (right >= 0 && nums[right] == target) return right;
return -1;
}

搜索旋转排序数组

数组在某个位置旋转过(如 [4,5,6,7,0,1,2])。

旋转数组二分查找
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;

// 判断哪半边是有序的
if (nums[left] <= nums[mid]) {
// 左半边有序
if (target >= nums[left] && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
// 右半边有序
if (target > nums[mid] && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}

常见面试问题

Q1: 二分查找的适用条件?

答案

  1. 有序(或局部有序,如旋转数组)
  2. 随机访问(数组,不是链表)
  3. 数据量足够大(小数据直接遍历更快)

Q2: 为什么用 left + (right - left) / 2 而不是 (left + right) / 2

答案

left + right 可能整数溢出(超过 Integer.MAX_VALUE)。left + (right - left) / 2 不会溢出。

相关链接