1.题目描述
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-search
2.题目分析
二分查找的过程说到底就是一个区间的变化的过程,思想描述起来是很简单的,就是在一个已经排好序的序列中查找的时候每次都会通过判断target
值应该在待查找序列的左半边还是右边来进行区间筛选之后再查找。
但是在具体实现编码的时候区间的变化过程确实会比较容易搞混,这个时候就要注意把握我们所设置的边界条件,也就是left
与right
之间的关系,它们之间的<
或者<=
应该是持续不变的。我们下面对两种情况分开讨论:
- 首先如果我们定义的条件是
left<right
,也就是我们工作的区间是[left,right)那么在target与中间值比较之后,我们更新区间就应该是target>Nmid
:left = mid+1
target<Nmid
:right=mid-1
- 如果定义的条件是
left<=right
,也就是我们工作的区间是[left,right]。那么就是如下的情况:target>Nmid
:left = mid+1
target<Nmid
:right = mid
为什么不同的边界条件下更新的区间不同呢?而且是只有right
的边界值更新才有区别呢?我们仔细观察其边界条件,会发现经过这样的边界变化,变化前后left和right构成的查值区间分别是[left,right]
和[left,right)
。没错,虽然它们的表达形式不一样,但是其区间内的值是一样的。所以不论我们是选择二者哪一种边界条件,只要我们把握这个不变量——区间内的可查找值,那就可以完成二分查找的过程,但是注意不能混用,如果在一种区间下使用了另一种区间下的边界更新,那就可能会导致死循环。
3.题目解答
3.1 左闭右闭区间
class Solution {
public:
int search(vector<int>& nums, int target) {
int left =0,right=nums.size()-1;
while(left<=right){
int mid = left+(right-left)/2;
if(target<nums[mid]){
right = mid-1;
}
else if(target>nums[mid]){
left = mid+1;
}
else return mid;
}
return -1;
}
};
3.2 左闭右开区间
class Solution {
public:
int search(vector<int>& nums, int target) {
int left =0,right=nums.size();
while(left<right){
int mid = left+(right-left)/2;
if(target<nums[mid]){
right = mid;
}
else if(target>nums[mid]){
left = mid+1;
}
else return mid;
}
return -1;
}
};
总结:区间控制要严格