转载

二分查找

比如我今天想写一个二分查找:

首先,很容易的写下 int bSearch(int begin, int end, int e)

然后,很自然的定义 int mid, left = begin, right = end;

接下来怎么写呢?while(left < right)?while(left <= right)?while(mid == left)?while(mid == right)?………………真正一个写程序人会想纠结好一会儿,我们就选一个吧while(left < right)

下面,也很自然,mid = (left + right) >> 1; 用位运算能节省一些时间呢!

现在呢?又是纠结的时候了吧?if(num[mid] > e)?if(num[mid] >= e)?我们也随便选一个吧,if(num[mid] > e)

其实,下面你会不断纠结……right = mid;这是正常人的写法,但是有时候也会看到别人写成right = mid - 1;我们也考虑下吧,但是现在我们就直接写right = mid;

有if了当然也会有else,然后理所当然 left = mid;同样记住还有一个选择left = mid + 1;

不错,整个while循环搞定了,最后就是返回了,写下return的时候是不是又纠结了?left?right?mid?算了,就写mid吧,整个程序就写好了,如下:

 1 int bSearch(int begin, int end, int e)  2 {  3     int mid, left = begin, right = end;  4     while(left < right)  5     {  6         mid = (left + right) >> 1;  7         if(num[mid] > e) right = mid;  8         else left = mid;  9     } 10     return mid; 11 }

呵呵呵呵呵。。。。。。。。。。。。。。

--------------------------------------我是吐槽和正解的分割线----------------------------------------------

其实吧二分查找这个东西我们真的忽略太多了。。。也只有当我写主席树的时候貌似才真正注意到不同给跪。。。

我简单结合一下STL中的lower_bound()谈谈理解:

 1 #include<iostream>  2 #include<cstdio>  3 #include<cmath>  4 #include<algorithm>  5 #include<queue>  6 #include<cstring>  7 #define PAU putchar(' ')  8 #define ENT putchar('/n')  9 using namespace std; 10 int A[100]; 11 int find1(float v){ 12     int L=0,R=7,M; 13     while(L<=R){ 14         M=L+R>>1;if(A[M]<v) L=M+1;else R=M-1; 15     } return L; 16 } 17 int find2(float v){ 18     int L=0,R=7,M; 19     while(L<R){ 20         M=L+R>>1;if(A[M]<v) L=M+1;else R=M; 21     } return L; 22 } 23 int find3(float v){ 24     int L=0,R=7,M; 25     while(L<=R){ 26         M=L+R>>1;if(A[M]<v) L=M+1;else R=M-1; 27     } return R+1; 28 } 29 int find4(float v){ 30     int L=0,R=7,M; 31     while(L<R){ 32         M=L+R>>1;if(A[M]<v) L=M+1;else R=M; 33     } return R; 34 } 35 int find5(float v){ 36     int L=0,R=7,M; 37     while(L<=R){ 38         M=L+R>>1;if(A[M]>v) R=M-1;else L=M+1; 39     } return L; 40 } 41 int find6(float v){ 42     int L=0,R=7,M; 43     while(L<R){ 44         M=L+R>>1;if(A[M]>v) R=M;else L=M+1; 45     } return L; 46 } 47 int find7(float v){ 48     int L=0,R=7,M; 49     while(L<=R){ 50         M=L+R>>1;if(A[M]>v) R=M-1;else L=M+1; 51     } return R+1; 52 } 53 int find8(float v){ 54     int L=0,R=7,M; 55     while(L<=R){ 56         M=L+R>>1;if(A[M]>v) R=M-1;else L=M+1; 57     } return R+1; 58 } 59 inline int read(){ 60     int x=0,sig=1;char ch=getchar(); 61     while(!isdigit(ch)){if(ch=='-')sig=-1;ch=getchar();} 62     while(isdigit(ch))x=10*x+ch-'0',ch=getchar(); 63     return x*=sig; 64 } 65 inline void write(int x){ 66     if(x==0){putchar('0');return;}if(x<0)putchar('-'),x=-x; 67     int len=0,buf[15];while(x)buf[len++]=x%10,x/=10; 68     for(int i=len-1;i>=0;i--)putchar(buf[i]+'0');return; 69 } 70 void init(){ 71     A[0]=5; 72     A[1]=5; 73     A[2]=6; 74     A[3]=6; 75     A[4]=6; 76     A[5]=9; 77     A[6]=9; 78     A[7]=10; 79     float cv=6; 80     write(find1(cv));ENT; 81     write(find2(cv));ENT; 82     write(find3(cv));ENT; 83     write(find4(cv));ENT; 84     write(lower_bound(A,A+8,cv)-A);ENT; 85     write(find5(cv));ENT; 86     write(find6(cv));ENT; 87     write(find7(cv));ENT; 88     write(find8(cv));ENT; 89     write(upper_bound(A,A+8,cv)-A);ENT; 90     return; 91 } 92 void work(){ 93     return; 94 } 95 void print(){ 96     return; 97 } 98 int main(){init();work();print();return 0;}

这是我写的十个简单的查找的函数。

二分查找

事实证明,这十个查找函数竟然全都是正确的,其中前五个返回了第一个重复的值,后五个返回了最后一个重复的值的地址+1。

至于速度,find1,2,5,6最快,STL最慢。

取左还是右,在于if()后面跟的是什么操作。

事实上,二分查找的变形还有许许多多。So,真正二分还是很神奇的,OI建议使用find1,移动光标速度会很快。(雾)

最后把主席树扔上来。。。

 1 #include<iostream>  2 #include<cstdio>  3 #include<cmath>  4 #include<algorithm>  5 using namespace std;  6 const int maxn=100000+10,maxnode=2000000+10;  7 int ls[maxnode],rs[maxnode],s[maxnode],root[maxn],A[maxn],num[maxn],ms,n,Q;  8 int find(int v){  9     int L=1,R=n,M; 10     while(L<=R){ 11         M=L+R>>1;if(num[M]<v) L=M+1;else R=M-1; 12     } return L; 13 } 14 void build(int x,int& y,int L,int R,int pos){ 15     s[y=++ms]=s[x]+1;if(L==R)return; 16     ls[y]=ls[x];rs[y]=rs[x];int M=L+R>>1; 17     if(pos<=M) build(ls[x],ls[y],L,M,pos); 18     else build(rs[x],rs[y],M+1,R,pos); return; 19 } 20 int query(int x,int y,int L,int R,int k){ 21     if(L==R) return L; 22     int M=L+R>>1,kth=s[ls[y]]-s[ls[x]]; 23     if(k<=kth) return query(ls[x],ls[y],L,M,k); 24     else return query(rs[x],rs[y],M+1,R,k-kth); 25 } 26 inline int read(){ 27     int x=0,sig=1;char ch=getchar(); 28     while(!isdigit(ch)){if(ch=='-') sig=-1;ch=getchar();} 29     while(isdigit(ch)) x=10*x+ch-'0',ch=getchar(); 30     return x*=sig; 31 } 32 inline void write(int x){ 33     if(x==0){putchar('0');return;}if(x<0) putchar('-'),x=-x; 34     int len=0,buf[15];while(x) buf[len++]=x%10,x/=10; 35     for(int i=len-1;i>=0;i--) putchar(buf[i]+'0');return; 36 } 37 void init(){ 38     n=read();Q=read();for(int i=1;i<=n;i++) num[i]=A[i]=read(); 39     sort(num+1,num+1+n);for(int i=1;i<=n;i++) build(root[i-1],root[i],1,n,find(A[i])); 40     return; 41 } 42 void work(){ 43     int a,b,c; 44     while(Q--){ 45         a=read();b=read();c=read(); 46         write(num[query(root[a-1],root[b],1,n,c)]); 47         putchar('/n'); 48     } 49     return; 50 } 51 void print(){ 52     return; 53 } 54 int main(){ 55     init();work();print();return 0; 56 }
正文到此结束
Loading...