1.朴素二分查找
.二分查找
二分查找思路:
1.left=0,right=nums.size()-1(最后一个元素下标),取中间元素下标 mid=left+(right-left)/2 (防溢出)2.nums[mid]>target ,说明mid右边的元素都大于target,就不用再从mid右边查找。right=mid-1
3.nums[mid]<target ,说明mid左边的元素都小于target,就不用再从mid左边查找。
left=mid+1
4.nums[mid]==tartget,找到目标值返回。
5.循环条件 left<=right,当left==right时,意味着只剩最后一个元素,有可能是目标值。
不断循环直到找到目标结果,或者left>right不满足循环条件。
class Solution {
public:
int search(vector<int>& nums, int target) {
int re=-1,left=0,right=nums.size()-1;
while(left<=right)
{
int i=left+(right-left)/2;
if(nums[i]>target) right=i-1;
else if(nums[i]<target) left=i+1;
else
return i;
}
return re;
}
};
2.查找区间左端点/右端点
在排序数组中查找元素的第一个和最后一个位置
这道题如果用朴素二分查找,虽然可以找到target==8的元素,但不一定就是目标值。
找到目标值再向左 向又查找不行吗?可以,但效率会降低。我们有其它方法可以更好的解决。
1.查找区间左端点
该方法对于朴素二分法不同的是,朴素二分法把区间分为3份,>target ==target <target。
查找区间左端点把区间分为2份,<target >=target(默认非递减)。
eg1.[5 7 7] [8 8 10] 而目标值就在右区间的最左边。
1.如果mid落在左区间,mid指向的元素肯定不是目标值,可以让left=mid+1
2.如果mid落在右区间,mid指向的元素有可能是目标值,所以让right=mid 如果-1可能会跳过目标值。
所以right只会在右区间移动,而left最远只会到右区间的最左边。所以当left==right就可以求出结果。
3.循环条件为left<right,不能是left<=right,如果等于会一直循环,left==right
mid=(left+right)/2 mid值不变,还是在右区间,right=mid right值也不变。
4.mid的取值,mid=left+(right-left) 还是向上取整 mid=left+(right-left+1)?有什么区别?
如果只剩两个数[5 7 7] [8 8 10] 的7 8。left指向7 right指向8,mid=left+(right-left+1) mid=1
此时mid指向8,落在右区间,right=mid。再取mid还是指向8,就会无限循环。
所以mid=left+(right-left) mid=0取靠左边的值,指向7,落在左区间,left=mid+1。此时left==right循环结束。
下面举2种极端情况
1.数组值全>target 只有右区间,right不断移动 left==0不动.left==right指向下标0
2.数组值全<arget 只有左区间,left不断移动 right==nums.szie()-1不动.left==right指向最后一个元素(因为mid=left+(right-left) mid=0取靠左边的值 不会出现mid==right left=mid+1越界的情况)
2.查找区间右端点
和查找左端点本质一样,但细节不同。
查找区间右端点把区间分为2份,<=target >target(默认非递减)。
eg1.[5 7 7 8 8] [ 10 ] 而目标值就在左区间的最右边。
1.如果mid落在右区间,mid指向的元素肯定不是目标值,可以让right=mid-1
2.如果mid落在左区间,mid指向的元素可能是目标值,所以让left=mid
所以left只会在左区间移动,而right最远只会到左区间的最右边。所以当left==right就可以求出结果。
3.循环条件为left<right,不能是left<=right,如果等于会一直循环,left==right
mid=(left+right)/2 mid值不变,还是在左区间,left=mid left值也不变。
4.mid的取值,mid=left+(right-left) 还是向上取整 mid=left+(right-left+1)?有什么区别?
如果只剩两个数[5 7 7 8 8 ][10] 的8 10。left指向8 right指向10,mid=left+(right-left) mid=0取靠左边的值 此时mid指向8,落在左区间,left=mid。再取mid还是指向8,就会无限循环。
所以mid=left+(right-left+1) mid=1取靠右边的值,指向10,落在右区间,right=mid-1。此时left==right循环结束。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int le=-1,ri=-1;
int left=0,right=nums.size()-1,mid=0;
//处理边界情况
if(nums.empty()) return {-1,-1};
//取左端点下标
while(left<right)
{
mid=left+(right-left)/2;//取靠左边的值
if(nums[mid]<target)
left=mid+1;
else
right=mid;
}
le=left;
left=0,right=nums.size()-1;
//取右端点下标
while(left<right)
{
mid=left+(right-left+1)/2;//取靠右边的值
if(nums[mid]<=target)
left=mid;
else
right=mid-1;
}
ri=left;
if(nums[left]==target) return {le,ri};
else return {-1,-1}; //没找到的情况
}
};
1.如果目标值是左端点,就把它套在右区间[5 7 7] [8 8 10],让左边的+1来找,mid就要取靠左的值
2.如果目标值是右端点,就把它套在左区间[5 7 7 8 8 ][10],让右边的-1来找,mid就要取靠右的值