二分查找

二分查找

二分查找是一种用于有序数列的折半查找算法。二分查找优点是比较次数少,查找速度快,平均性能好;时间复杂度为O(lgN)。因此二分查找也成为了面试中的常问问题。但是要写出一个完全正确的二分查找并不容易,下面我们先来看个错误的二分查找。(以下大部分是对编程珠玑章节的总结)

错误的二分查找

int binarySearch(vector<int> x,int t)
{
    int mid,l=0;
    int u = x.size()-1;
    while(l<=u)
    {
        mid = l+(u-l)/2;
        if(x[m]<t)
            l=m;
        else if(x[m]>t)
            u=m;
        else
            return m;
    }
    return -1;
}

代码中的错误很明显,因为每次用x[mid]比较之后,[l,u]的范围有可能不会减小,从而能到导致循环无限运行。

例如:范围为[2,3],对应的元素为[3,5],我们要找的元素为t=4。每次求得m=2,所以x[m]<4,因而l=m=2,没有发生变化。所以最终陷入了死循环。

正确的二分查找

额,虽然正确的二分查找已经很简单了,但这里还是列出来吧:

int binarySearch(vector<int> x,int t)
{
    int mid,l=0;
    int u = x.size()-1;
    while(l<=u)
    {
        mid = l+(u-l)/2;
        if(x[m]<t)
            l=m+1;
        else if(x[m]>t)
            u=m-1;
        else
            return m;
    }
    return -1;//没有找到对应的元素,返回-1.
}

恩,上述中循环每次都能保证范围[l,u]在减小,因此循环一定能够结束。其中用mid = l+(u-l)/2的写法而不是用mid=(u+l)/2 是为了保证元素不越界(编程之美上提到的)。

二分查找元素范围

leetcode上有一道题目是这样的:
Given a sorted array of integers, find the starting and ending position of a given target value.”意思是对于排好序的可能有重复元素的数组,找到某一个元素在数组中的位置范围。因此说要找到一个元素在数组中第一次出现的位置和最后一次出现的位置。

经过修改和代码调优我们可以在原有二分查找元素的基础上迅速实现上述题目要求的算法。首先看一下找打数组中第一次出现的位置。如下:

int getLeft(vector<int>& nums, int target)
{
        int left =-1;
        int right = nums.szie();//假设nums[-1]<target<=nums[nums.szie()]
        while(left +1<right )
        {
            int mid = left+(right-left)/2;
            if(nums[mid]<target)
                left = mid;
            else
                right =mid;
        }
        if(right>=nums.size() || nums[right] != target)
            right =-1;
        return right;
}

我们开始假设假设nums[-1]<target<=nums[nums.szie()] ,同时上述代码修改了判定的过程:

if(nums[mid]<target)
    left = mid;
else
    right =mid;

因此可以保证每次进行修改范围之后,都能满足nums[left]<target<=nums[right]。循环结束的条件是left = right-1; 所以若此时数组中存在target元素,那么right一定是对应该元素第一次出现的位置。这样的算法实现一定能保证在O(lgN)的时间复杂度。

同理,target元素最后出现的位置可用类似的代码实现:

int getRight(vector<int>& nums, int target)
{
        int left =-1;
        int right = nums.szie();//假设nums[-1]<=target<nums[nums.szie()]
        while(left +1<right )
        {
            int mid = left+(right-left)/2;
            if(nums[mid]>target)
                right = mid;
            else
                left =mid;
        }
        if(left<0 || nums[left] != target)
            left =-1;
        return left;
}

这里循环内部每次判定之后都是满足:假设nums[left]<=target<nums[right],因此循环结束后就能找到对应元素最后一次出现的位置。

总结

本文总结了二分查找的基本正确实现和二分范围查找,两者都能保证O(lgN)的时间复杂度;同时还点出了二分查找中容易出现的错误。写下此文以便以后迅速回顾撒。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值