二分

二分是个很实用的算法,每次运算区间均缩小一半,因此时间复杂度是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=5left=-1;   则mid=2; A[mid]>2;

  因此若r=mid-1=1就导致出错。

问题2:为什么不写成 While(left!=right) ?

  因为上面的原因我们没用 left=mid+1right=mid-1而是left=mid,right=mid。因此就会出现死循环的情况。

举个例子:

  arr[]={0,1,3,4}; 求找2的位置时会出现问题

  当left=1right=2时 mid=(left+right)/2=1; arr[mid]<2; 所以left=mid=1

  结果left还是等于1right还是等于2。死循环...

 代码解析:

原代码中left代表小于等于 x的一方 right代表大于x的一方

我们每次判断mid是属于left还是属于right来使区间缩小一半;

leftright相遇时,即left+1=right分界线就在leftright中间

则从left开始往前小于等于x,right开始往后大于x

同理其实我们也可以让

Iff(mid)<x

       left=mid;

else

       right=mid;

left代表小于x的一方而right代表大于等于x的一方

leftright相遇时;

则从left开始往前小于x,right开始往后大于等于x

 

原文地址:https://www.cnblogs.com/ashen137/p/10348888.html