二分是个很实用的算法,每次运算区间均缩小一半,因此时间复杂度是logN
下面给出简约版二分算法:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e6+7; 4 int n,m,l,arr[maxn]; 5 int find(int x) 6 { 7 int mid; 8 int left=-1;///开始坐标-1; 9 int right=n;///结束坐标+1; 10 while(left+1<right) 11 { 12 mid=(left+right)/2; 13 if(arr[mid]<=x) 14 left=mid; 15 else 16 right=mid; 17 } 18 return right; 19 } 20 int main() 21 { 22 scanf("%d",&n); 23 for(int i=0;i<n;i++) 24 scanf("%d",&arr[i]); 25 for(int i=0;i<n;i++) 26 printf("%d %d ",arr[i],find(arr[i])); 27 }
其中判断的结果产生的效果为:找到一个大于x的第一个位置(返回right);找到一个小于等于x的第一个位置(返回left);
问题1: 为什么不写成 left=mid+1和right=mid-1 ?
因为有可能arr[mid]已经是极限值。若执行right=mid-1的话,极限值的位置就被跳过了,从而导致出错。
举个例子:
设arr[]={0 1 3 4,5} 求找到大于2的第一个位置
当right=5,left=-1; 则mid=2; A[mid]>2;
因此若r=mid-1=1就导致出错。
问题2:为什么不写成 While(left!=right) ?
因为上面的原因我们没用 left=mid+1和right=mid-1而是left=mid,right=mid。因此就会出现死循环的情况。
举个例子:
设arr[]={0,1,3,4}; 求找2的位置时会出现问题
当left=1,right=2时 mid=(left+right)/2=1; arr[mid]<2; 所以left=mid=1;
结果left还是等于1,right还是等于2。死循环...
代码解析:
原代码中left代表小于等于 x的一方 right代表大于x的一方
我们每次判断mid是属于left还是属于right来使区间缩小一半;
当left与right相遇时,即left+1=right时分界线就在left和right中间
则从left开始往前小于等于x,从right开始往后大于x
同理其实我们也可以让
If(f(mid)<x)
left=mid;
else
right=mid;
left代表小于x的一方而right代表大于等于x的一方
当left与right相遇时;
则从left开始往前小于x,从right开始往后大于等于x