🎉 关注“青柠代码录”公众号,🔥更多精彩内容持续更新中🔥,快来加入我们吧!🚀
LeetCode题目链接:
目录
📝 文章摘要
在本篇中,我们将探讨如何利用二分查找算法解决一个经典的数组问题:找出目标值在非递减数组中的起始和结束位置。我们不仅会详细讲解解题思路,还会通过代码实现、案例分析以及图示帮助读者全方位理解这一问题。
🔍 解题思路
1. 问题分析
我们需要在一个非递减数组中找到目标值 target 的起始和结束位置。如果数组中不存在目标值,则返回 [-1, -1]。根据题目要求,算法的时间复杂度必须为 O(log n),这意味着我们需要使用二分查找来解决问题。
二分查找的核心思想是通过不断缩小区间范围,快速定位目标值的位置。由于我们需要找到目标值的起始和结束位置,因此需要对二分查找进行扩展,分别寻找第一个等于目标值的位置(左边界)和最后一个等于目标值的位置(右边界)。
2. 算法设计
(1)寻找左边界
-
初始化左右指针
left = 0和right = nums.length - 1。 -
在每次循环中计算中间位置
mid = left + (right - left) / 2。 -
如果
nums[mid] >= target,说明目标值可能在左侧或当前位置,因此更新right = mid - 1。 -
否则,更新
left = mid + 1。 -
循环结束后,检查
left是否有效(即是否越界且等于目标值),若满足条件,则left即为左边界;否则返回-1。
(2)寻找右边界
-
初始化左右指针
left = 0和right = nums.length - 1。 -
在每次循环中计算中间位置
mid = left + (right - left) / 2。 -
如果
nums[mid] <= target,说明目标值可能在右侧或当前位置,因此更新left = mid + 1。 -
否则,更新
right = mid - 1。 -
循环结束后,检查
right是否有效(即是否越界且等于目标值),若满足条件,则right即为右边界;否则返回-1。
3. 时间复杂度与空间复杂度
-
时间复杂度:O(log n)。每次二分查找将搜索范围缩小一半,因此时间复杂度为对数级别。
-
空间复杂度:O(1)。仅使用了常量级别的额外空间。
✍️ 代码实现
以下是完整的 Java 实现代码:
class Solution {
public int[] searchRange(int[] nums, int target) {
// 调用辅助函数分别寻找左边界和右边界
int leftIndex = findLeft(nums, target);
int rightIndex = findRight(nums, target);
// 返回结果
return new int[]{leftIndex, rightIndex};
}
// 寻找左边界
private int findLeft(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) {
right = mid - 1; // 目标值可能在左侧或当前位置
} else {
left = mid + 1; // 目标值一定在右侧
}
}
// 检查 left 是否越界且等于目标值
if (left < nums.length && nums[left] == target) {
return left;
}
return -1;
}
// 寻找右边界
private int findRight(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1; // 目标值可能在右侧或当前位置
} else {
right = mid - 1; // 目标值一定在左侧
}
}
// 检查 right 是否越界且等于目标值
if (right >= 0 && nums[right] == target) {
return right;
}
return -1;
}
}
🧩 案例执行过程
为了更好地理解算法的执行过程,我们通过几个具体案例进行分析。
案例 1:目标值存在
输入:
index: 0 1 2 3 4 5 // 索引位置 nums = [5, 7, 7, 8, 8, 10], target = 8
执行过程:
寻找左边界:
-
初始区间:
[0, 5],mid = 2,nums[mid] = 7,更新left = 3。 -
当前区间:
[3, 5],mid = 4,nums[mid] = 8,更新right = 3。 -
当前区间:
[3, 3],mid = 3,nums[mid] = 8,更新right = 2。 -
循环结束,
left = 3,检查nums[3] = 8,左边界为3。
寻找右边界:
-
初始区间:
[0, 5],mid = 2,nums[mid] = 7,更新left = 3。 -
当前区间:
[3, 5],mid = 4,nums[mid] = 8,更新left = 5。 -
当前区间:
[5, 5],mid = 5,nums[mid] = 10,更新right = 4。 -
循环结束,
right = 4,检查nums[4] = 8,右边界为4。
输出:
[3, 4]
案例 2:目标值不存在
输入:
nums = [5, 7, 7, 8, 8, 10], target = 6
执行过程:
寻找左边界:
-
初始区间:
[0, 5],mid = 2,nums[mid] = 7,更新left = 3。 -
当前区间:
[3, 5],mid = 4,nums[mid] = 8,更新right = 2。 -
循环结束,
left = 3,检查nums[3] != 6,左边界为-1。
寻找右边界:
-
初始区间:
[0, 5],mid = 2,nums[mid] = 7,更新left = 3。 -
当前区间:
[3, 5],mid = 4,nums[mid] = 8,更新right = 2。 -
循环结束,
right = 2,检查nums[2] != 6,右边界为-1。
输出:
[-1, -1]
案例 3:空数组
输入:
nums = [], target = 0
执行过程:
-
数组为空,直接返回
[-1, -1]。
输出:
[-1, -1]
🖼️ 图示解析
以下是对案例 1 的图示解析:
初始数组:[5, 7, 7, 8, 8, 10] 目标值:8 寻找左边界: [5, 7, 7, 8, 8, 10] --> [8, 8, 10] --> [8] 寻找右边界: [5, 7, 7, 8, 8, 10] --> [8, 8, 10] --> [8, 8]
🌈 总结
通过本文的学习,我们掌握了如何利用二分查找高效地解决“找出目标值的起始和结束位置”这一问题。从问题分析到算法设计,再到代码实现和案例执行,每一步都力求清晰易懂。希望本文能为你的学习之路提供帮助!🌟
🎉 感谢阅读!如果你觉得这篇文章对你有帮助,请不要吝啬点赞和分享哦! 🎉

被折叠的 条评论
为什么被折叠?



