关于三类二分法的探索

原来的做法

while(left<=right){
if(x>middle)
left = middle+1;
if(x<middle)
right = middle - 1
if(x == middle)
return middle;
}
else return -1

朴素的二分可以保证每回循环索引前进或者后退,保证不会死循环,所以可以用循环条件left<=right

改进:不return middle,每次都夹到只有一个元素

while(left<right-1 )
middle = (left+right)/2;
if(a[middle]>=x)
right = middle; //维护的性质a[right]>=x
else 
left = middle;//维护的性质a[left]<x

这样的结果:能够找到对于x,索引最小的位置;
但是因为以上过程没有等号,我们最后还需要一个等号判断找到的元素是否相等;

ifa[left]==x) return left;
else if(a[right] == x) return right;
else return -1;

思考:上面的循环条件能不能改成这样?

left<right-1
left<right
left<=right
left<=right-1

left<=right 肯定不行
因为对于左边的情况,我们没有让索引前进,所以如果遇到``
x==a[midle]
left=middle;
如果
right=middle+1;`
这个时候死循环就出现了;
以后的middle 都等于left

死循环的条件
考察middle的产生过程
注意mid = (left + right) /2
mid = left +(right-left)
其实是等价的(考虑left,right分别为奇数偶数的过程)

middle = (left+right)/2
死循环代表middle = left或者right 的过程一直持续下去
这个时候,只可能是left和right相邻,由于整数除法向下取整,所以middle永远等于left,导致死循环

while(left < right -1)看成一种模板

考察这种情况下循环结束的条件
因为left<right-1因此middle一直不会和a[left]或者a[right]相等

当最后一次进入循环,left = right -2然后left或者right会移动到middle,然后left与right相邻,这个时候会跳出循环 也就是说:跳出循环的条件是left和right相邻

一般性的方法

我们在维持while(left < right -1)的前提下,考察a[left]a[right]的性质

对于朴素的二分法
朴素的二分法其实是第一类二分法(找到满足x的最小下标)和第二类二分法(找到满足x的最大下标)的特殊情况

考察第一类二分法
第一类二分法需要找到满足x的最小下标
我们可以通过维护a[left]a[right]的性质来实现
通过使得a[right]>=x a[left]<x
(为什么这样?因为a[right]>=x 我们要找最小的时候,要往左找,找到最左边的a[i])
当跳出循环的时候a[left]<x只有a[right]可能满足x作为最小下标
所以我们判断
if(a[right] == x ) return right;
else return -1
完整的代码实现

 while(left < right -1){
    middle = ( right + left )/2;
    if( a[middle]>= x ) right = middle;   
    else  left = middle;
    }  
    if(a[right] == x) return right;
    else return -1;

考察第二类二分法
由于第二类二分法需要找到最大的满足的下标
所以需要a[left]<=x a[right]>x


while(left < right -1){
middle = (left+right)/2;
if(a[middle] <= x) left = middle;
else right = middle;
}
if(a[left] == x) return left;
else return -1;

对于朴素的二分法,选择以上一种就可以了

你可能感兴趣的:(关于三类二分法的探索)