二分查找核心算法(c++)

27 篇文章 0 订阅

1. 基本原理

注意点

  • 初始条件
  • 终止条件
  • 上下区间更新方法
  • 返回值选择

1.1 循环实现

/**
 * @brief 循环实现二分查找
 * @param nums 数据
 * @param target 查找目标
 * @return 找到目标则返回下标,否则返回-1
 */
int BSearch(const vector<int> &nums, int target) {
    int low = 0;
    int high = nums.size() - 1;
    
    while (low <= high) {
        int mid = low + ((high - low) >> 1);//这种写法不容易溢出且更加高效
        if (nums[mid] == target) return mid;
        else if (nums[mid] > target) high = mid - 1;
        else low = mid + 1;
    }
    return -1; //找不到
}

时间复杂度O(logn),空间复杂度O(1) 

1.2 递归实现

int BSearchRecursion(const vector<int> &nums, int low, int high, int target) {
    if (low > high) return -1;
    int mid = low + ((high - low) >> 1);
    if (nums[mid] == target) return mid;
    else if (nums[mid] > target) return BSearchRecursion(nums, low, mid-1, target);
    else return BSearchRecursion(nums, mid+1, high, target);
}

 时间复杂度O(logn),空间复杂度O(logn)

2. 简单应用

2.1 x的平方根

求x的平方根,要求精确到小数点后6位

double MySqrt(double x) {
    assert(x > 0);
    double low = 0;
    double high = x;
    double p = 1e-6;
    while (1) {
        double mid = low + (high - low) / 2;
        if ((mid+p)*(mid+p) > x && (mid-p)*(mid-p) < x) return mid;
        if (mid * mid > x) high = mid - p;
        else low = mid + p;
    }
}

 leetcode类似题目

class Solution {
public:
    int mySqrt(int x) {
        int low = 1;
        int high = x;
        while (1) {
            int mid = low + ((high - low) >> 1);
            if ((long long)mid*mid <= x && (long long)(mid+1)*(mid+1) > x) return mid;
            else if ((long long)mid*mid > x) high = mid - 1;
            else low = mid + 1;
        }
    }
};

时间复杂度O(logn),空间复杂度O(1) 

3. 变形应用

3.1 在排序数组中查找元素的第一个和最后一个位置

leetcode题目

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        // 1.查找第一个位置
        vector<int> ans{-1, -1};
        int low = 0;
        int high = nums.size() - 1;
        while (low <= high) {
            int mid = low + ((high - low) >> 1);
            if (nums[mid] == target) {
                if (mid == 0 || nums[mid-1] != target) { //此时左边没有等于nums[mid]的元素
                    ans[0] = mid;
                    break;
                }
                else {
                    high = mid - 1;
                }
            }
            else if (nums[mid] > target) high = mid - 1;
            else low = mid + 1;
        }
        // 没找到
        if (-1 == ans[0]) return ans;

        // 2.查找最后一个位置
        low = ans[0]; //加快搜索
        high = nums.size() - 1;
        while (low <= high) {
            int mid = low + ((high - low) >> 1);
            if (nums[mid] == target) {
                if (mid == nums.size() - 1 || nums[mid+1] != target) { //此时右边没有等于nums[mid]的元素
                    ans[1] = mid;
                    break;
                }
                else {
                    low = mid + 1;
                }
            }
            else if (nums[mid] > target) high = mid - 1;
            else low = mid + 1;
        }
        return ans;
    }
};

时间复杂度log(n),空间复杂度O(1) 

3.2 查找第一个大于等于给定值的元素

对应leetcode题目

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        // 找出第一个大于等于target目标的数,若没有返回尾后位置
        int low = 0;
        int high = nums.size() - 1;

        while (low <= high) {
            int mid = low + ((high - low) >> 1);
            if (nums[mid] < target) {
                low = mid + 1;
            }
            else if (mid == 0 || nums[mid-1] < target) { //若成立则是第一个大于等于target的元素
                return mid;
            }
            else {
                high = mid - 1;
            }
        }
        return nums.size(); // 不存在大于等于target的数,返回尾后位置
    }
};

3.3 循环序列中的二分查找问题 

搜索旋转排序数组 

关键在于找到特例情况 ,写法不一定最简洁, 但是容易理解

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int low = 0;
        int high = nums.size() - 1;

        while (low <= high) {
            int mid = low + ((high - low) >> 1);
            if (nums[mid] == target) return mid;
            else if (nums[mid] < target) {
                if (nums[mid] <= nums[high] && nums[high] <= nums[low] && nums[low] <= target){ //特例
                    high = mid - 1;
                }
                else {
                    low = mid + 1;
                }
            }
            else {
                if (target <= nums[high] && nums[high] <= nums[low] && nums[low] <= nums[mid]) { //特例
                    low = mid + 1;
                }
                else {
                    high = mid - 1;
                }
            }
        }
        return -1; //找不到
    }
};

时间复杂度O(logn),空间复杂度O(1) 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值