当前位置: 首页 > news >正文

LeetCode-34. 在排序数组中查找元素的第一个和最后一个位置

1、题目描述:

给你一个按照非递减顺序排列的整数数组 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

2、代码:

#include <vector>
using namespace std;

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        // 如果数组为空,直接返回 [-1, -1]
        if (nums.empty()) {
            return vector<int>{-1, -1};
        }

        // 调用辅助函数 findFirst 查找目标值的第一个位置
        int start = findFirst(nums, target);

        // 调用辅助函数 findLast 查找目标值的最后一个位置
        int end = findLast(nums, target);

        // 返回结果,包含起始位置和结束位置
        return vector<int>{start, end};
    }

    // 辅助函数:查找目标值的第一个位置
    int findFirst(vector<int>& nums, int target) {
        int l = 0, r = nums.size() - 1; // 定义左右边界
        int mid;

        // 开始二分查找
        while (l <= r) {
            mid = (l + r) / 2; // 计算中间值

            // 如果找到目标值
            if (target == nums[mid]) {
                // 检查是否是第一个出现的位置
                if (mid == 0 || nums[mid - 1] < target) {
                    return mid; // 确保这是第一个位置,返回索引
                }
                // 如果不是第一个位置,继续向左搜索
                r = mid - 1;
            }
            // 如果目标值大于当前值,说明目标值在右侧,向右搜索
            else if (nums[mid] < target) {
                l = mid + 1;
            }
            // 如果目标值小于当前值,说明目标值在左侧,向左搜索
            else {
                r = mid - 1;
            }
        }

        // 如果循环结束仍未找到目标值,返回 -1
        return -1;
    }

    // 辅助函数:查找目标值的最后一个位置
    int findLast(vector<int>& nums, int target) {
        int l = 0, r = nums.size() - 1; // 定义左右边界
        int mid;

        // 开始二分查找
        while (l <= r) {
            mid = (l + r) / 2; // 计算中间值

            // 如果找到目标值
            if (target == nums[mid]) {
                // 检查是否是最后一个出现的位置
                if (mid == nums.size() - 1 || nums[mid + 1] > target) {
                    return mid; // 确保这是最后一个位置,返回索引
                }
                // 如果不是最后一个位置,继续向右搜索
                l = mid + 1;
            }
            // 如果目标值大于当前值,说明目标值在右侧,向右搜索
            else if (nums[mid] < target) {
                l = mid + 1;
            }
            // 如果目标值小于当前值,说明目标值在左侧,向左搜索
            else {
                r = mid - 1;
            }
        }

        // 如果循环结束仍未找到目标值,返回 -1
        return -1;
    }
};

3、解题思路:

1. 核心思想

由于数组是有序的,可以利用二分查找的特性:

  • 起始位置 :目标值的第一个出现位置。
  • 结束位置 :目标值的最后一个出现位置。

为了满足时间复杂度 O(logn) 的要求,我们需要分别对这两个位置进行两次独立的二分查找。(也就是说,可以用借助2个辅助函数来实现两个位置的查找

2. 特殊情况处理

  • 空数组 :如果 nums.empty(),直接返回 [-1, -1]
  • 目标值不存在 :如果通过二分查找未找到目标值,返回 [-1, -1]

3. 具体步骤

第一步:查找起始位置
  1. 初始化左右边界 l = 0r = nums.size() - 1
  2. 使用二分查找:
    • 计算中间值 mid = (l + r) / 2
    • 如果 nums[mid] == target
      • 检查是否是第一个出现的位置(即 mid == 0nums[mid - 1] < target)。
      • 如果是,返回 mid
      • 如果不是,说明前面可能还有目标值,继续向左搜索,更新右边界 r = mid - 1
    • 如果 nums[mid] < target,说明目标值在右侧,更新左边界 l = mid + 1
    • 如果 nums[mid] > target,说明目标值在左侧,更新右边界 r = mid - 1
  3. 如果循环结束仍未找到目标值,返回 -1
第二步:查找结束位置
  1. 初始化左右边界 l = 0r = nums.size() - 1
  2. 使用二分查找:
    • 计算中间值 mid = (l + r) / 2
    • 如果 nums[mid] == target
      • 检查是否是最后一个出现的位置(即 mid == nums.size() - 1nums[mid + 1] > target)。
      • 如果是,返回 mid
      • 如果不是,说明后面可能还有目标值,继续向右搜索,更新左边界 l = mid + 1
    • 如果 nums[mid] < target,说明目标值在右侧,更新左边界 l = mid + 1
    • 如果 nums[mid] > target,说明目标值在左侧,更新右边界 r = mid - 1
  3. 如果循环结束仍未找到目标值,返回 -1

4. 时间与空间复杂度分析

  • 每次二分查找的时间复杂度为 O(logn)。
  • 分别查找起始位置和结束位置,总共进行了两次二分查找,总时间复杂度仍为 O(logn)。
  • 算法只使用了常量级别的额外空间(如变量 l, r, mid),因此空间复杂度为 O(1)。

相关文章:

  • 本地开发用ASP.NET Core Web API项目创建及测试
  • AF3 中扩散模型类总结
  • 使用C++实现简单的TCP服务器和客户端
  • 山东大学软件学院nosql实验四
  • docker离线安装及部署各类中间件(x86系统架构)
  • 【Microsoft PowerPoint for Mac】2分钟配置-MAC一键删除PPT中的所有备注
  • MTK Android12 预装apk可卸载
  • 一文讲解Redis中和本地缓存之间的一致性问题及区别等
  • 用Deepseek直接在word中完成论文的润色(中-中,中-英, 英-中)
  • 如何安装VMware
  • 基于不完整多模态神经影像的阿尔茨海默病诊断领域特定信息保留|文献速递-医学影像人工智能进展
  • 深度学习训练平台建设中的性能优化实践
  • [特殊字符]清华大学:DeepSeek从入门到精通.pdf(清华领航,驾驭DeepSeek,开启AI新境界)
  • ZT11 压缩二维码
  • 【目标检测】目标检测中的数据增强终极指南:从原理到实战,用Python解锁模型性能提升密码(附YOLOv5实战代码)
  • Mac下VSCode调试skynet的lua环境配置
  • 【前端基础篇】Day 1
  • Postman操作(接口测试、生成测试报告、MockServer等)
  • 检查模型配置参数
  • 软件测试的基础入门(一)
  • 做移动网站优化优/个人网页制作教程
  • 创建网站大约多少钱/seo推广软件品牌
  • 找做废薄膜网站/房地产网站建设
  • 提出网站推广途径和推广要点/青岛关键词网站排名
  • 微信公众号的微网站开发/优化法治化营商环境
  • 免费网站建设协议/友情链接平台网站