Hot100-Day13-T34在排序数组中查找元素的第一个和最后一个位置

5 阅读2分钟

Day13[26/3/13]T34在排序数组中查找元素的第一个和最后一个位置

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

解题思路

先用二分查找,找到元素是否存在。

如果存在,记录上一次的 left 和 right。

然后在 (left, middle)中寻找左侧边界,

同理在 (middle, right)中寻找右侧边界。

此外你需要考虑,前面说的都是有前提的:

nums[left] < target < nums[right]

如果是等于呢?如果是范围之外呢?

Code

#include <iostream>
#include <vector>

using namespace std;

class Solution
{
public:
    vector<int> searchRange(vector<int> &nums, int target)
    {
        // 1. 先检查有无元素,获得一个大概区间
        // 2. 再在小区间内,两次查找,找到左右边界
        vector<int> result{-1, -1};

        int left = 0;
        int right = nums.size() - 1;
        // 记录上次位置,供后续使用
        int last_left;
        int last_right;

        if (right == -1)
        {
            return {-1, -1};
        }

        if (nums[left] == target)
        {
            if (nums[right] == target)
            {
                return {left, right};
            }

            // 只需要找右侧边界
            while (left + 1 < right)
            {
                int middle = (left + right) / 2;
                if (nums[middle] == target)
                {
                    left = middle;
                }
                else
                {
                    right = middle;
                }
            }
            return {0, left};
        }
        else if (nums[right] == target)
        {
            if (nums[left] == target)
            {
                return {left, right};
            }

            // 只需要找左侧边界
            while (left + 1 < right)
            {
                int middle = (left + right) / 2;
                if (nums[middle] == target)
                {
                    right = middle;
                }
                else
                {
                    left = middle;
                }
            }
            return {right, (int)nums.size() - 1};
        }
        // 如果目标值不是在最左或者最右的情况,先考虑是不是直接范围外
        else if (target < nums[left] || target > nums[right])
        {
            return {-1, -1};
        }
        else // 若在范围内的处理
        {

            while (left + 1 < right)
            {
                int middle = (left + right) / 2;
                if (nums[middle] == target)
                {
                    // 此时已经找到了,保留原有的边界,继续查找
                    last_left = left;
                    last_right = right;
                    break;
                }
                else if (nums[middle] < target)
                {
                    left = middle;
                }
                else
                {
                    right = middle;
                }
            }

            // 确认是有元素的才行
            if (nums[(left + right) / 2] != target)
            {
                return result;
            }

            // 在小区间内搜索准确边界
            // 先搜左边界
            right = (last_left + last_right) / 2;
            while (left + 1 < right)
            {
                int middle = (left + right) / 2;
                if (nums[middle] == target)
                {
                    right = middle;
                }
                else
                {
                    // 此时  nums[middle] < target
                    // 不存在 nums[middle] > target
                    left = middle;
                }
            }
            result[0] = right; // 记录左侧边界
            // 再搜右边界
            right = last_right;
            left = (last_left + last_right) / 2;
            while (left + 1 < right)
            {
                int middle = (left + right) / 2;
                if (nums[middle] == target)
                {
                    left = middle;
                }
                else
                {
                    // 此时  nums[middle] > target
                    // 不存在 nums[middle] < target
                    right = middle;
                }
            }
            result[1] = left;
        }

        return result;
    };
};

auto main() -> int
{
    vector<int> nums{5, 7, 7, 8, 8, 10};
    int target = 7;

    // vector<int> nums{5, 5, 5, 5, 5, 5};
    // int target = 5;

    Solution sol;

    cout << "result = " << endl;
    cout << "[";
    for (const auto &num : sol.searchRange(nums, target))
    {
        cout << num << ",";
    }
    cout << "]" << endl;
}