一.题目

二.思路
提到二分查找 ,很多人第一反应就是"数组必须有序"。其实,这种说法并不准确。二分查找真正的前提是数组具有二分性 。那么,什么是二分性呢?
简单来说,如果你能在数组中找到一个点,将数组划分为三个部分:左区域 、该点 、右区域 ,并且能够根据某种规则确定目标位于哪一侧,那么这个数组就具备了二分性 。之所以我们常说"有序才能二分",是因为有序性 是最常见、最简单的规律------它满足左区域 < 中点 < 右区域 ,从而可以轻松判断目标在左边还是右边。可以说,有序只是二分性的一种特例,因为它足够简单,所以流传最广。
三.代码演示
cpp
class Solution {
public:
int search(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size()-1;
while(left <= right)
{
//left+(right - left)/2防止溢出
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;
}
};
四.代码讲解
第一步:问题理解与前提
-
本题给定一个升序排列 的整数数组
nums和一个目标值target,要求返回target在数组中的索引,如果不存在则返回 -1。 -
由于数组有序,具备二分性 ,我们可以利用二分查找算法高效求解。
第二步:初始化左右指针
-
定义左指针
left = 0,指向数组第一个元素。 -
定义右指针
right = nums.size() - 1,指向数组最后一个元素。 -
这两个指针用于维护当前搜索的区间,初始为整个数组。
第三步:循环查找
-
使用
while循环,条件为left <= right。
为什么是<=而不是<?因为当
left == right时,区间内还有一个元素需要检查,如果此时退出循环就会漏掉该元素。使用<=可以确保区间内所有元素都被覆盖。
第四步:计算中间位置(防溢出)
-
中间位置
mid = left + (right - left) / 2。
为什么不直接用(left + right) / 2?当
left和right都很大时,left + right可能超过int范围导致溢出。使用left + (right - left)/2可以避免此问题,是安全的写法。
第五步:比较中间值与目标
-
如果
nums[mid] == target,说明找到目标,直接返回mid。 -
如果
nums[mid] < target,说明目标在右半区间 ,因为数组有序,所有比mid小的元素都在左边,而目标更大,所以更新left = mid + 1,舍弃左半部分。 -
如果
nums[mid] > target,说明目标在左半区间 ,更新right = mid - 1,舍弃右半部分。
第六步:循环结束
- 如果循环结束仍未返回,说明
target不在数组中,返回 -1。
重点总结
-
二分查找的核心:每次将搜索区间缩小一半,时间复杂度 O(log n)。
-
循环条件 :必须用
left <= right,确保最后一个元素被检查。 -
中间值计算 :使用
left + (right - left)/2避免溢出。 -
指针更新 :根据比较结果,将
left或right移动到mid的相邻位置,因为mid已经检查过,无需再包含。
这个基础二分查找是所有变体(如寻找左右边界、旋转数组等)的基石,理解每一步的逻辑至关重要