题目
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1
题目链接
二分查找
应用场景
一.1.有序数组 2.数组内元素不重复,否则使用二分查找法,返回的数组元素的下标可能不唯一
二.无序数组,先对数组进行排序,使之符合有序数组的定义
思想
一.通过改变左右边界,中间值,来调整查找的区间
二.中间值与目标值比较,相等即可返回相应元素在数组中的下标
代码实现(两种方法)
法一:左闭右闭区间
// (法一) 左闭右闭区间 [left, right]
int search(int* nums, int numsSize, int target){
int left = 0;
int right = numsSize-1;
int middle = 0;
while(left<=right) {
middle = (left+right)/2;
if(nums[middle] > target) {
right = middle-1;
}
else if(nums[middle] < target) {
left = middle+1;
}
else if(nums[middle] == target){
return middle;
}
}
//若未找到target元素,返回-1
return -1;
}
法二:左闭右开区间
int search(int* nums, int numsSize, int target){
//左闭右开区间,左右边界
int left=0;
int right=numsSize;
while(left<right){
int middle=left+(right-left)/2;
if(nums[middle]==target){
return middle;
}
else if(nums[middle]>target){
right=middle;
}
else {
left=middle+1;
}
}
return -1;
}
疑惑点
while循环的条件中 <= 和 <
<=,闭区间
一,在数学中,表示一个闭区间,比如[1,1],是合法的。闭区间包含了所有大于等1且小于等于1的实数,鱿鱼实数不能同时大于等于1和小于等于1,所以这个区间只包含一个元素,就是实数1。因此,[1,1]表示的是只包含数值1的集合
<,左边右开区间
二,[1,)表示一个包含大于等于1但小于1的所有实数的集合。然而,实数不能同时满足大于等于1和小于1,所以这个集合是空集,也就是没有任何元素。
middle = (left+right)/2和middle = left+(right-left)/2的区别
(left+right)/2
求中间元素,这个相信大家没什么疑问
left+(right-left)/2
这里需要涉及精度溢出的概念
在计算机科学和数学中,数字通常以有限位数的二进制形式表示,而这种有限性可能导致数值范围的限制。当计算涉及超出表示范围的数字时,就可能发生精度溢出。
如果left,right本身很大,那么他们的和left+right必然更大,这个更大的数可能超出了int类型能表示的数值范围,这会带来错误
因此,为了避免错误更换运算方式,即left+(right-left)/2,做差不会造成数值变大,反而数值变小,这就规避了整型溢出的风险
right-left表示区间的长度
(right-left)/2表示区间长度的一半,可以看作是左边界的偏移量
左边界+偏移量(left+(right-left)/2), 等价于(left+right)/2