1 经典二分搜索(Classical Binary Search)

1 题目

  经典二分搜索(Classical Binary Search)

lintcode:题号——457,难度——easy

2 描述

  在一个排序数组中找一个数,返回该数出现的任意位置,如果不存在,返回 -1。

样例 1:

输入:nums = [1,2,2,4,5,5], target = 2
输出:1 或者 2

样例 2:

输入:nums = [1,2,2,4,5,5], target = 6
输出:-1

3 思路

  对有序数组可以直接使用二分法,即拿着目标元素与中间位置的元素进行比较,判断目标元素在当前位置的左区间还是右区间,再在目标区间内再次执行二分法,直到把子区间缩小到只有一个元素,即可找到或者不存在。

  1. 找到中间位置的元素;
  2. 与目标元素比较,确定目标元素所在的区间,缩小目标区间;
  3. 重复以上操作,直到找到或者结束。

3.1 图解

中间位置元素'5',大于目标元素'4'
中间位置元素'3',小于目标元素'4'
中间位置元素'4',等于目标元素'4'
有序数组'1, 2, 3, 4, 5, 6, 7, 8, 9' 目标元素'4'
缩小区间至'1, 2, 3, 4, 5'
抛掉'6, 7, 8, 9'
抛掉'1, 2'
缩小区间至'3, 4, 5'
找到目标元素'4'

3.2 时间复杂度

  算法的时间复杂度为O(log n)。

3.3 空间复杂度

  算法的空间复杂度为O(1)。

4 模版

  算法虽然简单,但是在边界的处理上如果不注意的话很容易就产生“死循环”、“漏元素”、“越界”的情况,产生的原因有很多,例如:

  1. 计算机的数据运算都是偏左的(给整数赋值“4.9”会被自动转成“4”,不是四舍五入)
  2. 比较大小的时候取“>”、“<”、">="、">=",会产生不同效果
  3. 缩小区间时候取不取当前的中间元素,有可能导致死循环
  4. 在数组的头部和尾部要考虑越界的情况

  为解决上述问题,分享一个通用的二分法算法的模版,需要注意的点有四个,分别如下:

  1. mid = start + (end - start) / 2
  2. start + 1 < end
  3. A[mid] ==, <, >
  4. A[start] A[end] ? target
  1. 取中点的时候使用start + (end - start) / 2,不使用(start + end) / 2,防止startend很大的时候,start + end在整型范围内越界;
  2. 循环退出条件使用start + 1 < end,不使用start < end,防止死循环;
  3. 循环内的判断使用==<>单独处理各个分支,不使用组合符号<=>=,防止死循环;
  4. 因为第二点的原因,所以循环退出后需要单独判断一次当前的startend位置的元素。

5 源码

  注意事项:

  1. 最后是与target比较,不要写错成与start、end比较;
  2. return的是index还是value要看清楚。

  C++版本:

/**经典二分搜索算法
* @param nums 有序数组
* @param target 目标元素的值
* @return 目标元素在有序数组中的位置(下标序号)
*/
int findPosition(vector<int> &nums, int target) {
    // write your code here
    if (nums.empty())
    {
        return -1;
    }
    
    int start = 0;
    int end = nums.size() - 1;
    int mid = 0;
    while (start + 1 < end) // 第二点:循环条件
    {
        mid = start + (end - start) / 2; // 第一点:取中点元素
        if (nums.at(mid) == target) // 第三点:单独处理各个分支
        {
            return mid;
        }
        if (nums.at(mid) < target)
        {
            start = mid;
        }
        if (nums.at(mid) > target)
        {
            end = mid;
        }
    }
    
    if (nums.at(start) == target) // 第四点:处理循环退出后的start和end
    {
        return start;
    }
    
    if (nums.at(end) == target)
    {
        return end;
    }
    
    return -1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值