二叉查找
我们学习的时候一般使用这个查找算法,在有序数组中(递增递减都行)来查找某一个数
int search(vector<int> &nums,int target)
{
int left=0;
int right=nums.size();
while(left<right)
{
int mid=left+(right-left)/2;
if(nums[mid]==target)
{
return mid;
}
else if(nums[mid]>target)
{
right=mid-1;
}
else if(nums[mid]<target)
{
left=mid+1;
}
}
}
// int mid=left+(right-left)/2; 这样的操作和左右相加再除以二 等价,但是有效的防止了left和right相加过大,导致的溢出情况
但是实际题目中,并不会直接考你二分查找算法怎么写,而是考察你有多个相同的数,要求得到索引最小或索引最大的,这就是左边界问题和右边界问题。
求左边界
//这些题解中的函数都是一个,只是具体方法不同,在下面的代码中,我就只写出函数体了
int left=0;
int right=nums.size();
while(left<right)
{
int mid=(left+right)/2;
if(nums[mid]==target) right=mid;
else if(nums[mid]<target) left=mid+1;
else if(nums[mid]>target) right=mid;
}
if(left==nums.size()) return -1;
else if(nums[left]==target) return left;
else if(nums[left]!=target) return -1;
首先 while判断语句中,用大于或者大于等于都行,只是具体细节方面需要调整,个人喜欢不加上等号,所以用的是不加上等号的版本。
不加上等号,那么while循环的截止条件是left==right 那么left能取到的范围是[0,nums.size()]左右都能取到,但是因为我们求得是索引,所以到不了nums.size();
所以在后面,得加上补丁防止数组越界
if(left==nums.size()) return -1;
若我们查找得数组中不存在这个值的话,也需要进行一次判断
else if(nums[left]==target) return left;
else if(nums[left]!=target) return -1;
求右边界
int left=0;
int right=nums.size();
while(left<right)
{
int mid=(left+right)/2;
if(nums[mid]==target) left=mid+1;
else if(nums[mid]<target) left=mid+1;
else if(nums[mid]>target) right=mid;
}
if(left-1<0) return -1;
else if(nums[left-1]==target) return left-1;
else if(nums[left-1]!=target) return -1;
为什么最后返回的是left-1?
因为在中值和目标值相等之后,left又进行加一操作,所以就可能导致nums[left]!=target了
为了防止第一个就是想要查找的值,即数组越界问题,所以我们需要进行一次判断,防止越界
补丁为:
if(left-1<0) return -1;
总结
这个方法,在二分后分成的数组是[left,mid], [mid+1,right]
和一般的版本不同,用这种分法是为了更好的找到边界问题
求左边界和求右边界方法差不多,唯一的区别是
求右边界时:
当 nums[mid] == target
时,不要立即返回,而是增大「搜索区间」的左边界 left
,使得区间不断向右靠拢,达到锁定右侧边界的目的。
求左边界时:
当 nums[mid] == target
时,不要立即返回,而是增大「搜索区间」的右边界right
,使得区间不断向左靠拢,达到锁定左侧边界的目的。
注意:
注意「搜索区间」和 while 的终止条件,如果存在漏掉的元素,记得在最后检查