二分查找基础模板
704.二分查找
思路
能够使用二分法的前提是给定的数组是升序数组或者是降序数组(有序数组),同时如果题目中强调数组中是没有重复元素的,那么使用二分法返回的元素下标就会是唯一的。
二分法主要涉及的麻烦问题在于边界条件,即到底是使用while(left<right)
还是while(left<=right)
,是使用left=middle
还是left=middle+1
(right也是同理)。
解决上述边界问题的关键点在于我们如何设定left和right。一般情况下left和right有两种设定方式:
1.left和right涵盖了题目所给数组的左闭区间和右闭区间,即假设数组下标从0开始,长度为n,则涵盖了左闭区间和右闭区间的含义是left=0,right=n-1。
2.left和right涵盖了题目所给数组的左闭区间和右开区间,即假设数组下标从0开始,长度为n,则涵盖了左闭区间和右开区间的含义是left=0,right=n。
首先我们假定想要取得的目标值为target。
对于第一种设定方式,二分法应该如下操作:
因为第一种设定方式target在[left,right]区间,所以在设置while(left<=right)
时,是要加等于号的,因为加上等于号,区间仍然是有意义的。
同时在设置left=middle+1
和right=middle-1
时,是要进行对应的加或者减的,因为当if(nums[middle]<target)
或者是if(nums[middle]>target)
时,根据判断式,我们已经可以知道middle这个下标所代表的数组的值是不等于target的,所以我们在设置left或者right时,需要将其去除。
根据上述的解释,我们可以得到第一种二分法的代码:
class Solution {
public int search(int[] nums, int target) {
int left=0;
int right=nums.length-1;
while(left<=right)
{
int middle=left+((right-left)/2); //防止溢出,其实就是(left+right)/2
if(nums[middle]>target)
{
right=middle-1;
}
else if(nums[middle]<target)
{
left=middle+1;
}
else{
return middle;
}
}
return -1;
}
}
对于第二种设定方式,二分法应该如下操作:
因为第二种设定方式中target是在[left,right)区间,所以在设置while(left<right)
时,是不能加上等于号的,因为加上等于号会使得区间变得没有意义。
同时在设置left=middle+1
和right=middle
时,left是需要进行对应的加一的,因为left所代表的是闭区间,而right是不需要进行减一的,因为right所代表的是开区间。(具体的解释和第一种方式相同)
根据上述的解释,我们可以得到第二种二分代码:
class Solution {
public int search(int[] nums, int target) {
int left=0;
int right=nums.length;
while(left<right)
{
int middle=left+((right-left)/2);
if(nums[middle]>target)
{
right=middle;
}
else if(nums[middle]<target)
{
left=middle+1;
}
else
return middle;
}
return -1;
}
}