重新认识二分查找算法

自己也曾写过二分查找算法,可是认识却有点模糊,或者说对算法的流程理解的并不是很透彻。《编程珠玑》里提到用循环不变式的三个性质来检验算法的正确性,即:

1.初始化:保证循环开始前初始条件是正确的。

2.保持:如果在循环的某一次迭代开始之前它是正确的,那么,在下一次迭代开始之前,它也应该保持正确。

3.终止:循环能够终止,并且可以得到期望的结果。

int BinSearch(int *array, int size, int key)  //前置条件是数组已是升序排列
{
    if(!array)
        return -1;
    int left = 0, right = size, mid;
    while(left <= right)
    {
        mid = left + (right - left)/2;    //或右移一位,这种写法是为了防止溢出
        if(array[mid] < key)
            left = mid + 1;
        else if(array[mid] > key)
            right = mid - 1;
        else
            return mid;
    }
    return -1;
}

如果key在数组中可能重复,要求返回key出现的第一个下标

int BinSearchFirst(int *array, int size, int key)  //前置条件是数组已是升序排列
{
    if(!array)
        return -1;
    int left = 0, right = size, mid;
    while(left < right)
    {
        mid = left + (right - left)/2;    //或右移一位,这种写法是为了防止溢出
        if(array[mid] >= key)
            right = mid;     //循环减少right-mid,如果right==mid(此时构成死循环),有right==left,因此循环终止条件不包括==
        else
            left = mid + 1;  //循环减少mid+1-left,至少减少为1
    }
    if(array[left] == key)   //终止时有left>=right,若left>right,则left上一步有left=mid+1,mid+1>right,退出mid==right,left==right,循环一开始就不会出现
        return left;         //若left==right,如果array[left]==key,返回left即可
    return -1;
}

对于要求返回最后一个等于key的位置x的算法,情况下更复杂一些.当array[mid]==key时,x可能存在于(mid,…,right)中,此时令left=mid,循环减少mid-left。由于mid通过除法运算舍去小数部分后更靠近left,所以mid==left时有left+1==right或left==right,不同于求第一次出现位置时的right==mid只有一种情况,与上例的主要区别也就在这里。因此循环终止条件应设为left<right-1.

int BinSearchLast(int *array, int size, int key)  
{
    if(!array)
        return -1;
    int left = 0, right = size, mid;
    while(left < right-1)
    {
        mid = left + (right - left)/2;   
        if(array[mid] <= key)
            left = mid;
        else
            right = mid - 1;
    }
    if(array[right] == key)
        return right;
    else if(array[left] == key)
        return left;
    else
        return -1;
}

递归版本的二分搜索原理与迭代版本相同,需要注意递归的不变式为判断语句,迭代为循环。

//递归版本的二分搜索
int Search(int *array, int left, int right, int key)
{
    int mid = left + (right-left)/2;
    if(left <= right)
    {
        if(array[mid] < key)
            return Search(array, mid+1, right, key);
        else if(array[mid] > key)
            return Search(array, left, mid-1, key);
        else 
            return mid;
    }
    else
        return -1;
}
int BinSearch(int *array, int size, int key)
{
    if(!array)
        return -1;
    int left = 0, right = size;
    return Search(array, left, right, key);
}






你可能感兴趣的:(重新认识二分查找算法)