您的位置:首页 > 其它

二分法专题-lintcode二分法问题解惑

2017-11-27 09:02 211 查看
二分法是常考的算法,适用于:在一个有序的序列中,找第一个满足某条件的位置(当然也可以找满足某条件的最后一个位置)

学习过程中让人迷惑的问题有:

上下限的确定,low=0,high=n-1还是low=0,high=n
循环条件low<high还是 low<=high
循环中low=mid还是low=mid+1,high=mid还是high=mid-1
有些情况无法退出循环,即是要mid=(low+high)/2还是mid=(low+high+1)/2    (即向左偏还是向右偏)
通过例子来帮助理解,不需要死记硬背

最普通的二分查找

int findPosition(vector<int> &nums, int target) {
// write your code here
//二分查找
if(nums.size()==0) return -1;
int low=0,high=nums.size()-1;
int mid;
while(low<=high){
mid=low+(high-low)/2;
if(nums[mid]==target) return mid;
if(nums[mid]<target){
low=mid+1;
}
else{
high=mid-1;
}
}
//循环结束,没有返回,则不存在
return -1;
}

上下限要覆盖所有的可能,故n-1就足够了,当然扩大一些,上限为n也可以
这里要找等于target的数,这样写循环结束后low>high,[low,high]无法构成区间,通过此判断是否存在,故写成low<=high

第一个满足某条件的位置,最后一个满足某条件的位置

我们将最后一个满足某条件a的位置转换为求第一个!a的位置,再-1即可,可避免一些问题



即求最后一个满足a<=sqrt(x)的数,可以直接求,也可以转化为第一个满足a>sqrt(x)的位置,然后再减1
我们先分析第二种的写法

low=0,上限是多少?high=x当然没错,但可以更小一些。sqrt(x)不可能超过x/2,即求sqrt(100)不会超过50,sqrt(101)不会超过50.5,但c++中除法会得到50,故x/2+1。但注意,我们现在是要求a>sqrt(x)的位置,故还要向后放一个,x/2+2。总结下:直接求a<=sqrt(x)的最后一个数,low=0,high=x/2+1;求a>sqrt(x)的第一个位置:low=0,high=x/2+2
若mid*mid<=x则low=mid+1,因为我们要找的是a>sqrt(x)的第一个位置,mid*mid<=x后是取不到mid的故low=mid+1而不是low=mid,同理,mid*mid>x时,可以取到mid,high=mid
是否需要右偏?不需要,因为循环条件low<high,最后low,high到了相邻位置,(low+high)/2向左偏,到了low,可以通过low=mid+1破坏循环条件,从而退出
代码如下:
int sqrt(int x) {
// write your code here
//将上界放为x/2+2
long low=0,high=x/2+2;
long mid;
//这里尝试一种更好理解的方法:找到第一个mid*mid>x的数,再-1即可
//一旦使用这种方法,上界需要改为x/2+2
while(low<high){
mid=low+(high-low)/2;
if(mid*mid<=x){
low=mid+1;
}
else{
high=mid;
}
}
//返回夹出的位置-1
return low-1;
}


直接求a<=sqrt(x)的最后一个位置:
区别在于上限为x/2+1,mid*mid<x则low=mid(因为这里mid可能就是<sqrt(x)的最后一个数),mid*mid>x,则high=mid-1。这里只能通过high=mid-1破坏low<high的循环条件,故应该右偏mid=(low+high+1)/2,最后注意应该返回high
所以求最后一个位置还是推荐转化为第一个位置-1
int sqrt(int x){
long low=0,high=x/2+1;
long mid;

while(low<high){
//注意这里的二分,要向右偏
//如果不向右偏,x=50,low=7,high=8,会一直循环下去;
//向右偏,low=7,high=8,然后mid=8>50故high=7,返回7即可
mid=low+(high-low+1)/2;
if(mid*mid==x){
return mid;
}
else if(mid*mid<x){//sqrt在右侧,注意这里可能等于mid,不应写low=mid+1
low=mid;
}
else{//sqrt在左侧
high=mid-1;
}
}
//注意这里不能返回low
return high;
}

其他类似题目



即找第一个>=target的位置,再找第一个>target的位置-1



先对第0列的数二分,找最后一个<=target的数的位置即第一个>target的位置-1,这样就可以确定target在哪行,然后对这一行用最基本的二分查找即可
类似的题目还有很多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息