参考链接
花花酱哔哩哔哩视频
二分搜索的模板
正如bfs、dfs有模板,二分搜索也有模板。
模板一:左闭右开 [l,r)
public void bianrySearch(int l ,int r){
while(l<r){
int m = l+((r-l)>>1);
if(f(m)) return m;//不一定有
if(g(m)){
r = m;//new range [l,m)
}else{
l = m+1;//new range [m+1,r)
}
}
return l;//是g(m)成立的最小值
}
模板二:左闭右闭 [l,r]
public void bianrySearch(int l ,int r){
while(l<=r){
int m = l+((r-l)>>1);
if(f(m)) return m;//不一定有
if(g(m)){
r = m-1;//new range [l,m)
}else{
l = m+1;//new range [m+1,r)
}
}
return l;//是g(m)成立的最小值
}
对于左闭右开、左闭右闭,只是在细节上有些区别,最后结果是一样的。
g(x)是一个函数,g(x)满足 如果x>=m,g(x)>0为true;否则g(x)>0为false。
核心思想是:不要试图去找一个正确答案。试图去找一个分割点m,使得x>=m,g(x)>0为true。
对于不同类型的题目,核心工作就是寻找g(x)。
在我自己看来比较难理解的是最后求得的l是满足g(x)的最小值。我的理解是g(m)不满足条件,l=m+1,那l就是最可能的满足g(x)的最小值。即使在没有值满足g(x)的情况下,那l 会等于r或者r+1。
demo1 First Bad Version
278. First Bad Version
查找第一个是坏版本的版本号。这里g(x) = isBadVersion(x).
public int firstBadVersion(int n) {
int l = 1,r = n;
while(l<=r){
int m = l+((r-l)>>1);
if(isBadVersion(m)){
r = m-1;//new range [l,m)
}else{
l = m+1;//new range [m+1,r)
}
}
return l;//是g(m)成立的最小值
}
demo2 my sqrt
my sqrt需要返回最大的一个数y,使得
y
2
<
=
x
y^2<=x
y2<=x。
g
(
y
)
=
y
2
−
x
>
0
g(y)=y^2-x>0
g(y)=y2−x>0
public int mySqrt(int x) {
if(x<2) return x;
long l = 0,r = x>>1;
long answer = l;
while(l<=r){
long middle = l+ ((r-l)>>1);
if(middle*middle-x>0){
r = middle - 1;
}else{
l = middle+1;
}
}
return (int)(l-1);
}