2/3 寻找目标出现的初始or最后位置(First Position of Target/Last Position of Target)

1 题目

  寻找目标出现的初始位置(First Position of Target)

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

2 描述

  给定一个排序的整数数组(升序)和一个要查找的整数 target,用O(log n)的时间查找到target第一次出现的下标(从0开始),如果target不存在于数组中,返回-1。

  样例:

输入:数组 = [1,4,4,5,7,7,8,9,9,10], target = 4
输出:1

3 思路

  从头开始遍历需要的时间复杂度是O(n),要求在O(log n)的时间内完成,可以用二分法解决。

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

  与之前的经典二分搜索[1]大致相同,可以套用经典二分搜索的模版,需要注意的差异如下:

  1. 循环中与目标元素的比较在“>”和“==”时候的处理,都是抛掉右边的部分;
  2. 跳出循环之后,对剩下的两个元素的检查顺序是从左到右。

3.2 图解

graph TD A[有序数组'1, 4, 4, 5, 7, 7, 8, 9, 9, 10' 目标元素'4'] -- 中间位置元素'7',大于目标元素'4' --> B A --> A1[/抛掉'7, 8, 9, 9, 10'/] B[缩小区间至'1, 4, 4, 5, 7'] -- 中间位置元素'4',小于等于目标元素'4' --> C B --> B1[/抛掉'5, 7'/] C[缩小区间至'1, 4, 4'] -- 中间位置元素'4',小于等于目标元素'4' --> D D[缩小区间至'1, 4'] -- 在头尾元素中依次寻找目标元素 --> E E(找到目标元素'4')

3.2 时间复杂度

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

3.3 空间复杂度

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

4 源码

  C++:

/**
* @param nums: 有序数组
* @param target: 目标元素的值
* @return: 目标元素在有序数组中的初始位置(下标序号)
*/
int binarySearch(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) // 大于和等于的情况处理方式相同
        {
            end = mid;
        }
        else
        {
            start = mid;
        }
    }
    
    if (nums.at(start) == target) // 先判断左边的是否是目标元素
    {
        return start;
    }
    if (nums.at(end) == target)
    {
        return end;
    }
    
    return -1;
}

5 相同类型问题

  题目:

寻找目标出现的最后位置(Last Position of Target)
lintcode:题号——458,难度——easy

  C++版本:

/**
* @param nums: 有序数组
* @param target: 目标元素的值
* @return: 目标元素在有序数组中的最后位置(下标序号)
*/
int lastPosition(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)
        {
            start = mid;
        }
        else
        {
            end = mid;
        }
    }
    
    if (nums.at(end) == target)
    {
        return end;
    }
    
    if (nums.at(start) == target)
    {
        return start;
    }
    
    return -1;
}

  1. 经典二分搜索:https://blog.csdn.net/SeeDoubleU/article/details/118271548 ↩︎

posted @ 2021-06-30 11:07  seedoubleu  阅读(114)  评论(0)    收藏  举报