二分查找:逐步缩小搜索区间
使用场景:
1.数组为有序排列
2.数组中没有重复元素
题目:力扣704题
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
解题思路:
1.理解题意:
由题意得:
数组的特点:有序,升序,整型
分析如下四种情况,最后两种情况相似,可以看成一种:
方式一:目标值在数组中
方式二:目标值不在数组中,目标值位于数组中两个相邻值之间
方式三:目标值不在数组中,且目标值大于数组中所有的值
方式三:目标值不在数组中,且目标值小于数组中所有的值
2.解题方法
2.1定义两个指针,left和right
left为数组最左侧的边界(也是起始索引):一般开始为0
left = 0;
right为数组最右侧的边界(末端索引):一般由数组长度(nums.length)决定
right = nums.length - 1;
2.2while 循环判断
2.2.1当 left <=right时,执行以下步骤:
区间范围:[left,right]
2.2.1.1上述区间范围里,中间值的中间索引:
该算法防止出现溢出现象:
int middle = left + (right - left)/2;
2.2.1.2判断目标值所在的区间:
①当目标值与数组中索引为middle的值相等时:
nums[middle] = target
便在数组中找到了这个数,返回其索引即可:
return middle;
②当目标值小于数组中索引为middle的值时:
nums[middle] > target
我们就可以得出结论:
目标值target < nums[middle]
此时我们需要向左继续缩小目标值的区间:
缩小区间的范围为:[left,middle)
因为while循环的条件要求left要小于等于right,
左边界不变,
右边界:
right =middle - 1;
所以区间的范围就要修改为:[left,right]
③当目标值大于数组中索引为middle的值时:
nums[middle] < target
我们就可以得出结论:
目标值target > nums[middle]
此时我们需要向右继续缩小目标值的区间:
缩小区间的范围为:(middle,right]
因为while循环的条件要求left要小于等于right,
右边界不变,
左边界:
left =middle + 1;
所以区间的范围就要修改为:[left,right]
2.2.1.3继续执行2.2.1.1的步骤
2.2.1.4如果当left=right时,nums[middle]不等于target,说明数组中没有该值
2.2.1.5当left>right时,退出while循环,返回-1
2.3数字代入:
方式一:
索引index = 0 1 2 3 4 5
数组nums: [-1 , 0 , 3 , 5 , 9 , 12]
目标值target =9
① left =0 right =5
区间范围:[0,5]
left<right
middle =left+(right-left)/2=2
nums[2] =3<target
可知:目标值在索引为2的值的右边
向右缩小区间范围:即区间的左边界向右移动
left =middle+1=3
区间范围:[3,5]
②left =3 right =5
left<right
middle =left+(right-left)/2=4
nums[4] =target=9
在数组中找到目标值:
返回索引middle =2
方式二:
索引index = 0 1 2 3 4 5
数组nums: [-1 , 0 , 3 , 5 , 9 , 12]
目标值target =2
① left =0 right =5
区间范围:[0,5]
left<right
middle =left+(right-left)/2 =2
nums[2] =3>target
可知:目标值在索引为2的值的左边
向左缩小区间范围:即区间的右边界向左移动
right = middle-1=1
区间范围:[0,1]
②left = 0 right = 1
left<right
middle =left+(right-left)/2=0
nums[0] =-1<target
可知:目标值在索引为0的值的右边
向右缩小区间范围:即区间的左边界向右移动
left = middle+1=1
区间范围:[1,1]
left = 1 right = 1
left = right
middle =left+(right-left)/2=1
nums[1]=0<target
可知:目标值在索引为1的值的右边
向右缩小区间范围:即区间的左边界向右移动
left = middle+1=2
区间范围:[2,1]
此时left>right,退出循环
在数组中没有找到目标值:返回-1
方式三和方式四类似:此处以方式三为例:
索引index = 0 1 2 3 4 5
数组nums: [-1 , 0 , 3 , 5 , 9 , 12]
目标值target =13
① left =0 right =5
区间范围:[0,5]
left<right
middle =left+(right-left)/2 =2
nums[2] =3<target
可知:目标值在索引为2的值的右边
向左缩小区间范围:即区间的左边界向右移动
left = middle+1=3
区间范围:[3,5]
②left = 3 right = 5
left<right
middle =left+(right-left)/2=4
nums[4] =9<target
可知:目标值在索引为4的值的右边
向右缩小区间范围:即区间的左边界向右移动
left = middle+1=4
区间范围:[4,5]
left = 4 right = 5
left<right
middle =left+(right-left)/2=4
nums[4]=9<target
可知:目标值在索引为4的值的右边
向右缩小区间范围:即区间的左边界向右移动
left = middle+1=5
区间范围:[5,5]
left = 5 right = 5
reft = right
middle =left+(right-left)/2=5
nums[5]=12<target
可知:目标值在索引为5的值的右边
向右缩小区间范围:即区间的左边界向右移动
left = middle+1=6
区间范围:[6,5]
此时left>right,退出循环
在数组中没有找到目标值:返回-1
代码:
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(right >= left){
int middle = left + (right - left)/2;
if(nums[middle] == target){
return middle;
}else if(nums[middle] > target){
right = middle - 1;
}else{
left = middle + 1;
}
}
return -1;
}
}
结语:
著此篇,与君享,互勉之,共进步!