二分查找
二分查找是一种我们在初中就学过的效率较高的用对数级别的时间复杂度进行遍历的算法,关于这个算法,有很多细节的地方需要注意,参考labuladong老师的《算法小抄》 现总结如下:
1:找目标值的二分查找
例如:
nums=[1,2,3,4,5,6,7];
target=3;
求:target的索引值。
那么这个问题,有两个方法,1:暴力遍历,时间复杂度是O(n)
for(int i=0;i<nums.size();i++)
{
if(nums[i]==target)
{
return i;
}
}
上述代码的逻辑非常简单。显然,根据我们初中学过的二分查找,可以更快,时间复杂度更低(logN)级别的。
我将代码及其逻辑梳理如下
int binarysearch(vector<int> nums,int target)
{ if(nums.size()==0) return -1;
int left=0;
int right=nums.size()-1;//从这里的定义我们可以看到,右边的指针指向数组元素的最后一个元素;
while(left<=right)//因为搜索区间是一个[a,b]左闭右闭的区间,所以终止条件是[right+1, right],这样的一个区间是没有元素的,然而如果写成(left<right),那这种情况下mid就是这俩索引中其中的一个值,就会漏掉另外一个;
{
int mid=left+(right-left)/2;//这种写法可以有效的防止溢出;
if(nums[mid]==target)
{
return mid;
}
else if(nums[mid]<target)
{
left=mid+1;//如果nums[mid]<target,那么应该在[mid+1,right]的区间中接着找
}
else if(nums[mid]>target)
{
right=mid-1;//如果nums[mid]>target,那么应该在[left,mid-1]中接着找
}
}
return -1;
}
第二种情况是寻找左侧边界的二分查找算法;
int left_bound_binarysearch(vector<int> nums,int target)
{
if(nums.size()==0) return -1;
int left=0;
int right=nums.size()-1;
while(left<=right)
{
int mid=left+(right-left)/2;
if(nums[mid]<target)
{
left=mid+1;
}
else if(nums[mid]==target)
{
right=mid-1;//关键在于这一步,如果找到了这个元素,不立马返回这个元素,而是向左缩小右侧边界,
}
else if(nums[mid]>target)
{
right=mid-1;
}
}
//由于退出while的条件是left=right+1;因此当target>数组中的所有元素时,left会越界,那么这种情况下需要检查越界情况
if(left>=nums.size()||nums[left]!=target)//由于在相等的情况下,right=mid-1,然后就是一直移动左边的指针,直到left=right+1,这种情况下,left一定会移动到target的位置,如果不是target的位置,那一定是溢出边界了;
{
return -1;
}
return left;
}
同理寻找右侧边界的代码也很简单
int right_bound_binarysearch(vector<int> nums,int target)
{
if(nums.size()==0) return -1;
int left=0;
int right=nums.size()-1;
while(left<=right)
{
int mid=left+(right-left)/2;
if(nums[mid]<target)
{
left=mid+1;
}
else if(nums[mid]==target)
{
left=mid+1;;//关键在于这一步,如果找到了这个元素,不立马返回这个元素,而是向右缩小左侧边界,
}
else if(nums[mid]>target)
{
right=mid-1;
}
}
//由于退出while的条件是left=right+1;因此当target<数组中的所有元素时,right会越界,那么这种情况下需要检查越界情况
if(right<0||nums[right]!=target)//这里同上述左侧检查边界情况一样,一直向右缩小左边界,当左边界==target时,这时left的操作是left=mid+1,然后一直缩小右边界,直到left=right+1时结束,如果右边界<0,或者nums[right]!=target,那一定是出界了;
{
return -1;
}
return right;
}