【算法拾光】二分查找|在排序数组中查找元素的第一个和最后一个位置 + 搜索旋转排序数组

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

题目链接:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

题目分析

题目:给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 -1, -1。 核心:分别二分查找左边界、右边界。左边界是第一个等于 target 的位置;右边界是最后一个等于 target 的位置。 边界注意:找到边界后,要校验边界是否合法、对应数值是否等于 target,防止 target 不存在。

算法原理

两次二分:

  1. 找左边界:nums[mid] >= target 收缩右边界
  2. 找右边界:nums[mid] <= target 收缩左边界,最后结果 - 1 才是最后一个 target 位置 拿到左右边界后必须校验,数组里没有 target 时返回 -1,-1

代码编写

cpp 复制代码
vector<int> searchRange(vector<int>& nums, int target) {
    int n = nums.size();
    int left = 0, right = n;
    // 找左边界
    while(left < right) {
        int mid = left + (right - left) / 2;
        if(nums[mid] >= target) right = mid;
        else left = mid + 1;
    }
    int l = left;
    // 找右边界
    left = 0, right = n;
    while(left < right) {
        int mid = left + (right - left) / 2;
        if(nums[mid] <= target) left = mid + 1;
        else right = mid;
    }
    int r = left - 1;
    if(l < n && nums[l] == target) return {l, r};
    return {-1, -1};
}

总结

重复有序数组专用,两次边界二分;校验是必写步骤,很多人漏这一步导致报错。


第二题:33. 搜索旋转排序数组

题目链接:33. 搜索旋转排序数组 - 力扣(LeetCode)

题目分析

题目:整数数组 nums 按升序排列,数组中的值互不相同。在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了旋转,使数组变为 nums \[k, nums k+1, ..., nums n-1, nums 0, nums 1, ..., nums k-1]。给你旋转后的数组 nums 和一个整数 target,如果 nums 中存在这个目标值,则返回它的下标,否则返回 -1。 核心:旋转数组一定有一半区间是有序的。判断左半段有序还是右半段有序,再看 target 是否落在有序区间内,以此收缩二分边界。

算法原理

旋转数组二段性:左右两段必有一段有序

  1. 判断左半段是否有序 nums[left] <= nums[mid]
    • 有序且 target 落在这个区间:去左边搜
    • 否则去右边搜
  2. 右半段有序
    • 有序且 target 落在这个区间:去右边搜
    • 否则去左边搜

代码编写

cpp 复制代码
int search(vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;
    while(left <= right) {
        int mid = left + (right - left) / 2;
        if(nums[mid] == target) return mid;
        // 左半有序
        if(nums[left] <= nums[mid]) {
            if(nums[left] <= target && target < nums[mid]) right = mid - 1;
            else left = mid + 1;
        } else {
            // 右半有序
            if(nums[mid] < target && target <= nums[right]) left = mid + 1;
            else right = mid - 1;
        }
    }
    return -1;
}

总结

旋转数组二分重点:先识别有序区间,再判断 target 是否在有序区间内,是面试高频题。

相关推荐
吴可可1231 小时前
DXF与AAMA格式转换模块推荐
算法
重生之小比特1 小时前
【算法拾光】二分查找|搜索旋转排序数组 + 寻找旋转排序数组中的最小值
算法
YukiRinLL1 小时前
查找与排序算法:复杂度、稳定性
数据结构·算法·排序算法
徐小夕10 小时前
花了3个月,我们做了一款 AI 版的“飞书文档”
前端·算法·github
Robert--cao10 小时前
移动底盘与机械臂联合规划:开源算法与工程实现
算法
小豆庄11 小时前
Mac 上给视频人脸打码:本地工具的依赖、追踪与推理管线
算法·视觉检测·视频·敏捷流程·纹理贴图
星夜离尘5611 小时前
MATLAB 工程实验:从相邻块均值手写重叠 Allan 偏差,数清有效项
算法·matlab·均值算法
careathers12 小时前
【数据结构】从堆到 PriorityQueue:手写一个优先级队列
数据结构
y1su13 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划