二分查找小结

参考资料:

     1、程序员编程艺术第二十五章
     2、勇幸的博客
二分查找除了参考以下代码外,参考lower_bound()upper_bound()两个函数的实现代码

  • 资料一的代码
//二分查找V0.1实现版
//copyright@2011 July
//随时欢迎读者找bug,email:zhoulei0907@yahoo.cn。

//首先要把握下面几个要点:
//right=n-1 => while(left <= right) => right=middle-1;
//right=n   => while(left <  right) => right=middle;
//middle的计算不能写在while循环外,否则无法得到更新。

int binary_search(int array[],int n,int value)
{
    int left=0;
    int right=n-1;
    //如果这里是int right = n 的话,那么下面有两处地方需要修改,以保证一一对应:
    //1、下面循环的条件则是while(left < right)
    //2、循环内当array[middle]>value 的时候,right = mid

    while (left<=right)          //循环条件,适时而变
    {
        int middle=left + ((right-left)>>1);  //防止溢出,移位也更高效。同时,每次循环都需要更新。

        if (array[middle]>value)
        {
            right =middle-1;   //right赋值,适时而变
        }
        else if(array[middle]<value)
        {
            left=middle+1;
        }
        else
            return middle;
        //可能会有读者认为刚开始时就要判断相等,但毕竟数组中不相等的情况更多
        //如果每次循环都判断一下是否相等,将耗费时间
    }
    return -1;
}
  • 资料二的代码
/* binsearch 寻找key下标,不存在 return -1 */

/* binsearch 注意点【找不到 vs 死循环】
 * 1. left <= right 如改为 left < right 可能找不到key
 *    例如 1 2 3 4 5;key=5; left==right时候才搜到key
 * 2. left = mid + 1;
 *    如上left改为=mid,可能死循环,例如上面例子,
 *    当left指向4时候,right指向5,此时,死循环;
 *    死循环的根本原因在于left,当两个指针left与right相邻
 *    left可能永远等于mid,而right不会因为等于mid死循环
 */
int binsearch(int * arr, int lef, int rig, int key)
{
    if(!arr)    return -1;
    int left = lef, right = rig;
    while(left <= right)
    {
        int mid = left + ((right-left)>>1);
        if(arr[mid] < key)
        {
            left = mid + 1;
        }else if(arr[mid] > key)
        {
            right = mid - 1;
        }else
            return mid;
    }
    return -1;
}

/* binsearch_min 返回key(可能有重复)第一次出现的下标,如无return -1
 *
 * binsearch_min 注意点【死循环】
 * 1. 如果while(left < right)改为(left <= right)可能死循环;
 * 2. 循环结束条件,left == right
 *
 * 该代码我测试了很多用例,没发现反例,我认为是对的
 * 但网上都是用的left<right-1的条件并分别对arr[left]和arr[right]
 * 进行检查;我认为我写的更简练,希望有兴趣的读者帮忙review这段代码
 * 如发现反例指出错误,感激不尽,嘿
 */
int binsearch_min(int * arr, int lef, int rig, int key)
{
    if(!arr)    return -1;
    int left = lef, right = rig;
    while(left < right)
    {
        int mid = left + ((right-left)>>1);
        if(arr[mid] < key)
        {
            left = mid+1;
        }
        else
        {
            right = mid;
        }
    }
    if(arr[left] == key)    return left;
    return -1;
}

/* binsearch_max 返回key(可能有重复)最后一次出现的下标,如无return -1
 *
 * binsearch_max 注意点【死循环 vs 越过目标位置】
 * 1. 如果改为while(left < right)可能死循环;
 * 2. 如果left=mid改为left=mid+1;则有可能越过目标位置
 * 3. 循环结束条件,left == right || left == right -1
 *
 * 如非要死记:找最大的等号放<=key的位置,找最小的等号放>=key位置
 */
int binsearch_max(int * arr, int lef, int rig, int key)
{
    if(!arr)    return -1;
    int left = lef, right = rig;
    while(left < right -1)
    {
        int mid = left + ((right-left)>>1);
        if(arr[mid] <= key)
        {
            left = mid;
        }
        else
        {
            right = mid;
        }
    }
    if(arr[right] == key) // 找max,先判断right
    {
        return right;
    }
    else if(arr[left] == key)
    {
        return left;
    }else
        return -1;
}

/* binsearch_justsmall 返回刚好小于key的元素下标,如无return -1
 *
 * binsearch_justsmall 注意点【死循环 vs 越过目标位置】
 * 1. 如果改为while(left < right)可能死循环;因为left=mid的缘故
 * 2. 如果left=mid改为left=mid+1;则有可能越过目标位置
 * 3. 循环结束条件,left == right || left == right -1
 */
int binsearch_justsmall(int * arr, int lef, int rig, int key)
{
    if(!arr)    return -1;
    int left = lef, right = rig;
    while(left < right - 1)
    {
        int mid = left + ((right-left)>>1);
        if(arr[mid] < key)
        {
            left = mid;
        }else
        {
            right = mid - 1;
        }
    }
    if(arr[right] < key) // 找刚好小于,先判断right
    {
        return right;
    }else if(arr[left] < key)
    {
        return left;
    }else
        return -1;
}

/* binsearch_justgreat 返回刚好大于key的元素下标,如无return -1
 *
 * binsearch_justgreat 注意点【死循环 vs 检查元素是否大于key】
 * 1. 如果改为while(left <= right)可能死循环;因为right = mid;
 * 2. 最后注意检查arr[right]是否大于key
 * 3. 循环结束条件,left == right
 */
int binsearch_justgreat(int * arr, int lef, int rig, int key)
{
    if(!arr)    return -1;
    int left = lef, right = rig;
    while(left < right)
    {
        int mid = left + ((right-left)>>1);
        if(arr[mid] <= key)
        {
            left = mid + 1;
        }else
        {
            right = mid;
        }
    }
    if(arr[right] > key) return right;
    return -1;
}

原文地址:https://www.cnblogs.com/ZhaoxiCheung/p/5692395.html