算法——二分查找

算法学习(一)

对于已经有序的数据,使用二分查找查找可以快速找到目标值或者目标位置。
二分查找算法的基本思想:在有序表中,每次都取中间记录作为比较对象,若给定值与中间记录的关键字相等则查找成功,返回该关键字的索引;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复上诉过程,直到查找成功,或者所有查找区域无记录,查找失败为止,返回一个值代表没有找到。

基本二分查找

一个基本的二分查找代码如下所示:

int binarySearch(vector<int>& nums, int target) {
    int left = 0; 
    int right = nums.size()- 1; // ①

    while(left <= right) {//②
        int mid = left + (right - left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            left = mid + 1; 
        else if (nums[mid] > target)
            right = mid - 1; 
    }
    return -1;
}

①.right值的设置为什么是size-1?
②.循环跳出条件为什么是left <= right?
结论:
如果right=size-1,那么循环条件用left <= right;
如果right=size,那么循环条件用left == right。
原因:
如果初始值right=size-1,那么初始的搜索区间就是[left,right],即左闭右闭,那么如果没有找到应该是left>right的时候,或者说left=right+1,此时搜索区间为[right+1,right],搜索区间为空。如果循环里面不包含等号,那么left==right的情况会被忽略,可能会漏解。
如果初始值right=size,那么初始的搜索区间就是[left,right),即左闭右开,那么如果没有找到应该是left等于right的情况,此时搜索区间是[right,right),该区间为空,因此不会漏解。

如果这个有序数组中存在重复的数组,那么在查找的时候会延伸出新的问题—— 重复数字的左右边界在哪里?

改版一:左侧边界

寻找左侧边界的思想是:在查找到目标数字后,并不是直接返回,而是进一步缩小右侧边界,直到循环终止。
那么,右侧边界应该如何收缩呢?
如果数字存在于数组中,那么在找到相等值后right边界会不断向左收缩,直到right索引位于target目标值的左边一个索引,而退出循环的条件刚好是left=right+1,刚好是target的左侧边界索引值!
因此,此时的left就是有效的返回值。
具体代码如下:

int leftBound(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size()-1; 
	// 搜索区间为 [left, right]
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            // 搜索区间为 [mid+1, right]
            left = mid + 1;
        } else if (nums[mid] > target) {
            // 搜索区间为 [left, mid-1]
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 收缩右侧边界
            right = mid - 1;
        }
    }
    // 检查出界情况
    if (left >= nums.size() || nums[left] != target)
        return -1;
    return left;
}

同理,查找右侧边界的代码就非常简单了。

改版二:右侧边界

int rightBound(vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 这里改成收缩左侧边界即可
            left = mid + 1;
        }
    }
    // 这里改为检查 right 越界的情况
    if (right < 0 || nums[right] != target)
        return -1;
    return right;
}

参考文献

https://blog.csdn.net/weixin_43373833/article/details/113763879

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值