二分法专题-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 (即向左偏还是向右偏)
通过例子来帮助理解,不需要死记硬背
上下限要覆盖所有的可能,故n-1就足够了,当然扩大一些,上限为n也可以
这里要找等于target的数,这样写循环结束后low>high,[low,high]无法构成区间,通过此判断是否存在,故写成low<=high
即求最后一个满足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破坏循环条件,从而退出
代码如下:
直接求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在哪行,然后对这一行用最基本的二分查找即可
类似的题目还有很多
学习过程中让人迷惑的问题有:
上下限的确定,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在哪行,然后对这一行用最基本的二分查找即可
类似的题目还有很多
相关文章推荐
- 彻底了解mapreduce核心Shuffle--解惑各种mapreduce问题
- C#解惑04: 初级问题
- Linux手机DIY.库文件专题.交叉编译的浮点问题
- 最大K乘积问题(DP专题)
- Tomcat运行时,报servlet.DispatcherServlet 的ClassCastException问题——待解惑
- Endnote专题之--output style相关问题
- 六、答疑解惑——背包问题之软考
- [kuangbin带你飞]专题一 简单搜索 -K - 迷宫问题
- 【转】关于Java String对象创建问题解惑
- SaaS专题(一)前途未卜:成本问题困扰SaaS前景
- 递推递归专题 D 汉诺塔系列问题
- Hyper-V常用问题解惑
- JAVA网络编程Socket常见问题 【长连接专题】
- POJ 1321 棋盘问题(kuangbin带你飞 专题一)
- 在论坛中出现的比较难的sql问题:22(触发器专题3)
- MySQL 死锁专题问题处理
- SAE上传web应用(包括使用数据库)教程详解及问题解惑
- 【Python专题】 使用Py3及Pycharm的一些小问题(仅个人问题记录)
- 【算法设计与数据结构】二分法解决最大值最小化问题——入门篇
- SDAU 搜索专题 05 转弯问题