LeetCode 704. 二分查找
题目描述

题意分析
在一个按升序排列、元素互不相同的整数数组
nums中查找target。如果存在,返回它的下标;否则返回-1。返回的是位置,不是元素值,也不是目标本应插入的位置。数组已经有序,因此比较一个位置后,可以判断目标只能出现在它的左侧还是右侧,无需逐个检查所有元素。
解法:闭区间二分查找
核心思路
[!blue]
用闭区间
[left, right]表示尚未排除的下标范围,两个端点都包含在内。始终维护一个条件:如果目标存在且还未被找到,它一定在这个区间中。开始时把整个数组作为候选范围,即left = 0、right = n - 1。每轮检查中点
mid。若nums[mid] == target,直接返回。若nums[mid] < target,由于数组升序,mid及其左侧的值都不可能等于目标,所以新范围从mid + 1开始。若nums[mid] > target,同理排除mid及其右侧,新右边界为mid - 1。每次删除的都是确定不可能的位置,上面的条件始终成立。
left == right时仍剩下一个尚未检查的位置,因此循环条件必须是left <= right。只有left > right才表示候选区间为空;此时还没找到目标,就能确定它不存在,返回-1。中点使用
left + (right - left) / 2,避免直接相加两个下标可能造成的整数溢出。每轮都会排除已检查的中点,并把候选范围缩小约一半,所以能够在对数时间内结束。
解题步骤
- 初始化闭区间
[0, nums.length - 1]。- 当
left <= right时,计算mid = left + (right - left) / 2。- 若中点值等于目标,返回
mid;若小于目标,令left = mid + 1;若大于目标,令right = mid - 1。- 区间为空后退出循环,返回
-1。
代码实现
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
// 闭区间剩下一个位置时,仍需检查最后一个候选。
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
}
if (nums[mid] < target) {
// 中点已经检查过,连同不可能的一侧一起排除。
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
}
func search(nums []int, target int) int {
left, right := 0, len(nums)-1
// 闭区间剩下一个位置时,仍需检查最后一个候选。
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return mid
}
if nums[mid] < target {
// 中点已经检查过,连同不可能的一侧一起排除。
left = mid + 1
} else {
right = mid - 1
}
}
return -1
}
复杂度分析
- 时间复杂度:$O(\log n)$,其中
n为数组长度。每轮只做常数次比较,并排除约一半候选位置。- 空间复杂度:$O(1)$,只维护左右边界和中点,不创建子数组。
关键点总结
[!green]
- 先确定候选区间是否包含端点,再统一初始化、循环条件和边界更新方式。
- 本实现采用闭区间,必须配套使用
right = n - 1、left <= right和mid ± 1。- 更新边界的依据是数组有序性:排除的整段都不可能包含目标,而非仅凭中点大小猜测方向。
易错点总结
[!yellow]
- 把循环条件写成
left < right,会漏查最后一个位置。- 把右边界初始化为数组长度,会访问越界。
- 更新为
left = mid或right = mid,可能导致死循环。- 未找到时应返回
-1,不要误返回插入位置left。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 35. 搜索插入位置 | 简单 | 本题可以在相等时返回,原题还要在不存在时给出插入边界。 |
| 34. 在排序数组中查找元素的第一个和最后一个位置 | 中等 | 有重复值时普通二分只找任意一个,本题的边界扩展可用于寻找最左与最右位置。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!