二分查找(Binary Search)是一种在有序数组中查找特定元素的查找算法。它通过将目标值与数组的中间元素进行比较,并根据比较结果缩小查找范围,直到找到目标值或确认目标值不存在为止。
一般步骤如下:
二分查找是一种高效的查找算法,时间复杂度为O(log n),其中n是数组的元素个数。但前提是要求数组必须是有序的。
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
样例输出:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
二分查找涉及的很多的边界条件,逻辑比较简单,但就是写不好。例如到底是 while(left < right) 还是 while(left <= right),到底是right = middle呢,还是要right = middle - 1呢?
写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。
二分法第一种写法
第一种写法,我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right]
区间的定义这就决定了二分法的代码应该如何写,因为定义target在[left, right]区间,所以有如下两点:
while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
代码实现:
//@i want to武动乾坤
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;//这里使用了闭区间。
int model=0;//定义中间位置
while(left<=right){
int model=left+((right-left)/2);//这里是为了防止溢出
if(nums[model]>target){
right=model-1;
}else if(nums[model]<target){
left=model+1;
}else{//否则就是所求的值等于了model位序对应的元素
return model;//返回所找到的序号
}
}
return -1;//如果循环了一遍发现要找的元素没有找到,返回-1
}
};
第二种方法
如果说定义 target 是在一个在左闭右开的区间里,也就是[left, right) ,那么二分法的边界处理方式则截然不同。
有如下两点:
while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size();//这里使用了左闭右开区间,所以让right=nums.size
int model=0;//定义中间位置
while(left<right){
int model=left+((right-left)/2);//这里是为了防止溢出
if(nums[model]>target){
right=model;//从0到model
}else if(nums[model]<target){
left=model+1;//左闭,所以这里的model小于目标值。
}else{//否则就是所求的值等于了model位序对应的元素
return model;//返回所找到的序号
}
}
return -1;//如果循环了一遍发现要找的元素没有找到,返回-1
}
};