• 二分查找及其变种


    二分查找

    普通二分查找

    首先说下普通二分查找的思路

    普通二分查找是在一个没有重复的排序数组中,找到目标值

    思路就是先从中间找,如果中间值大于目标值,说明目标值在左半区

    如果中间值小于目标值,说明目标值在右边,

    当中间值等于目标值,返回他的下标,

    //  这里的数组默认都不为空

    // 给定一个有序数组和一个关键字,找到该值在数组中的下标,否则返回-1
    int
    binary_search(vector<int>vec, int target) { int left = 0, right = vec.size()-1; while(left <= right) { // int mid = (left + right) >> 1; // 等价于mid = (left+right) 防止溢出 int mid = left + (right-left)/2; // 防止溢出 if (vec[mid] == target) return mid; else if (vec[mid] > target) // 在左半区 right = mid - 1; else left = mid + 1; //在右边区 } return -1; // 不存在 }

    一般人可能直接这样写:int mid = (left + right) / 2,不能说错误,但存在问题:如果 left 和 right 比较大的话,两者之和就有可能会溢出

    改进的方法是将 mid 的计算方式写成 left + (right-left)/2

    更进一步,如果要将性能优化到极致的话,我们可以将这里的除以 2 操作转化成位运算  left + (right-left)>>1

    因为相比除法运算来说,计算机处理位运算要快得多。

    二分查找变种写法

    当出现的目标值有重复的怎么找

    变种一:查找第一次出现的目标值

    我们对数组data进行二分,如果数组中间的数字小于k,说明k应该出现在中间位置的右边;
    如果数组中间的数字大于k,说明k应该出现在中间位置的左边;
    如果数组中间的数字等于k,并且中间位置的前一个数字不等于k,
    说明这个中间数字就是数字k出现的第一个位置。否则在左边找

    int binary_search(vector<int>vec, int target)
    {
        int left = 0, right = vec.size()-1;
        while(left <= right)
        {
            int mid = left + (right-left)/2;
            if (vec[mid] > target)
                right = mid -1;
            else if (vec[mid] < target )
                left = mid + 1;
            else
            {
                // 当目标值等于给定值时,如果mid是第一个元素或者前面没有目标值,就是要找的
                if(mid == 0 || vec[mid - 1] != target)
                    return mid;
                else right = mid - 1;  //注意这里
            }
        }
        return -1;
    }

    变种二:

    找出最后一个出现的目标数,思路和一类似

    //找最后一个出现的,同上面类似,只要修改相等的
    int binary_search(vector<int>vec, int target)
    {
        int left = 0, right = vec.size()-1;
        while(left <= right)
        {
            int mid = left + (right-left)/2;
            if (vec[mid] > target)
                right = mid-1;
            else if (vec[mid] < target)
                left = mid + 1;
            else
            {
                if (mid == vec.size()-1 || vec[mid+1] != target)
                    return mid;
                else
                    left = mid + 1;
            }
        }
        return -1;
    }
    #include <iostream>
    #include <vector>
    
    using namespace std;
    int binary_search1(vector<int>vec, int target)
    {
        int left = 0, right = vec.size()-1;
    
        while(left <= right)
        {
    //        int mid = (left + right) >> 1;  // 等价于mid = (left+right) 防止溢出
            int mid = left + (right-left)/2;  // 防止溢出
            if (vec[mid] == target)
                return mid;
            else if (vec[mid] > target)  // 在左半区
                right = mid - 1;
            else
                left = mid + 1;  //在右边区
        }
        return -1;  // 不存在
    }
    // 查找重复数组中第一个出现给定值
    /* 思路, 和普通的二分改变的是当等于给定值需要修改
    我们对数组data进行二分,如果数组中间的数字小于k,说明k应该出现在中间位置的右边;
    如果数组中间的数字大于k,说明k应该出现在中间位置的左边;
    如果数组中间的数字等于k,并且中间位置的前一个数字不等于k,
    说明这个中间数字就是数字k出现的第一个位置。
    */
    int binary_search2(vector<int>vec, int target)
    {
        int left = 0, right = vec.size()-1;
        while(left <= right)
        {
            int mid = left + (right-left)/2;
            if (vec[mid] > target)
                right = mid -1;
            else if (vec[mid] < target )
                left = mid + 1;
            else
            {
                // 当目标值等于给定值时,如果mid是第一个元素或者前面没有目标值,就是要找的
                if(mid == 0 || vec[mid - 1] != target)
                    return mid;
                else right = mid - 1;
            }
        }
        return -1;
    }
    //找最后一个出现的,同上面类似,只要修改相等的
    int binary_search3(vector<int>vec, int target)
    {
        int left = 0, right = vec.size()-1;
        while(left <= right)
        {
            int mid = left + (right-left)/2;
            if (vec[mid] > target)
                right = mid-1;
            else if (vec[mid] < target)
                left = mid + 1;
            else
            {
                if (mid == vec.size()-1 || vec[mid+1] != target)
                    return mid;
                else
                    left = mid + 1;
            }
        }
        return -1;
    }
    int main()
    {
        // 连续不重复的数组,普通的二分查找
        vector<int> vec1 = {1,2,3,4,5,6};
        cout << binary_search1(vec1, 4) << endl;
    
        // 含有重复的,找出第一个等于给定值的下标
        vector<int> vec2 = {1,2,2,2,2,6};
        cout <<"第一次出现2的下标" <<binary_search2(vec2, 2) << endl;
    // 含有重复的,找出最后一个等于给定值的下标
        cout <<"最后一次出现2的下标" <<binary_search3(vec2, 2) << endl;
        return 0;
    }
    完整代码

    普通二分也可以递归实现

    // 二分查找的递归实现
    int bSearch1(vector<int>a, int n, int value) {
      return bSearchInternally(a, 0, n - 1, value);
    }
     
    int bSearchInternally(vector<int>a, int low, int high, int value) {
      if (low > high) return -1;
     
      int mid =  low + ((high - low) >> 1);
      if (a[mid] == value) {
        return mid;
      } else if (a[mid] < value) {
        return bSearchInternally(a, mid+1, high, value);
      } else {
        return bSearchInternally(a, low, mid-1, value);
      }
    }
  • 相关阅读:
    2013.11.18 流水
    return to blog!
    IOS实现毛玻璃效果的三种方式
    UI常用控件总结(三)
    UI常用控件总结(二)
    UI常用控件总结(一)
    UIView 常见属性
    OC语言BLOCK和协议
    OC语言类的深入和分类
    OC语言构造方法
  • 原文地址:https://www.cnblogs.com/xiaokang01/p/12450117.html
Copyright © 2020-2023  润新知