參考鏈接
花花醬嗶哩嗶哩視頻
二分搜索的模板
正如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,使得。
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;
}