文章目录
-
- 一、题目描述
- 二、问题分析
- 三、思维导图
- 四、解法一:标准二分法思路
-
- [1. 核心思路 🧠](#1. 核心思路 🧠)
- [2. 流程图](#2. 流程图)
- 五、代码实现(Java)
- 六、复杂度分析
- [七、解法二:先找旋转点 + 普通二分查找](#七、解法二:先找旋转点 + 普通二分查找)
-
- [1. 思路要点](#1. 思路要点)
- [2. 时序图(查找旋转点与搜索过程)](#2. 时序图(查找旋转点与搜索过程))
- [3. 实现代码(Java)](#3. 实现代码(Java))
- 八、对比分析
一、题目描述
给定一个旋转排序数组 nums(升序排序后某个位置被旋转),以及一个目标值 target,请你在数组中寻找该目标值。如果存在,返回它的下标;否则返回 -1。
示例:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
二、问题分析
一个「旋转排序数组」其实是一个被分成两个有序子数组的数组,例如:
原始升序数组: [0,1,2,3,4,5,6]
旋转后可能变为: [4,5,6,0,1,2,3]
数组依然包含两个有序区间:
- 左区间:递增
- 右区间:递增
我们可以用二分查找的思想进行搜索,只需在二分过程中判断当前区间的性质(是否有序),再决定搜索方向。
三、思维导图
Search in Rotated Sorted Array
原理
二分查找
判断有序区间
决定搜索方向
情况分析
左半部分有序
右半部分有序
实现思路
mid 与 left/right 比较
target 范围判断
移动 left 或 right
复杂度
时间 O(log n)
空间 O(1)
四、解法一:标准二分法思路
1. 核心思路 🧠
在每轮二分中:
- 计算中点
mid; - 判断左半段是否有序;
- 如果左半段有序:
- 判断目标是否在左半段范围内;
- 若是,则移动右指针;
- 若否,则移动左指针;
- 如果右半段有序:
- 判断目标是否在右半段范围内;
- 若是,则移动左指针;
- 若否,则移动右指针。
2. 流程图
是
否
是
是
否
否
是
否
开始
计算 mid = (left + right) / 2
nums[mid] == target?
返回 mid
nums[left] <= nums[mid]?
target 在左区间?
right = mid - 1
left = mid + 1
target 在右区间?
left = mid + 1
right = mid - 1
继续循环
结束,未找到返回 -1
五、代码实现(Java)
java
public class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = (left + right) / 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;
}
}
六、复杂度分析
| 类型 | 分析 |
|---|---|
| 时间复杂度 | O(log n) --- 因为每次都折半查找 |
| 空间复杂度 | O(1) --- 仅使用常数级变量 |
七、解法二:先找旋转点 + 普通二分查找
1. 思路要点
如果我们能先找到旋转点,即最小值所在的位置 ,就能确定左右两段的有序区间。
接着分别在对应的区间使用常规二分查找。
2. 时序图(查找旋转点与搜索过程)
数组 算法 数组 算法 查找旋转点 ->> 最小值位置 判断 target 在左/右区间 根据区间进行二分搜索 返回索引或 -1
3. 实现代码(Java)
java
public class Solution {
public int search(int[] nums, int target) {
int pivot = findPivot(nums);
if (nums[pivot] == target) return pivot;
if (target >= nums[0]) {
return binarySearch(nums, 0, pivot - 1, target);
} else {
return binarySearch(nums, pivot, nums.length - 1, target);
}
}
private int findPivot(int[] nums) {
int left = 0, right = nums.length - 1;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] > nums[right]) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
private int binarySearch(int[] nums, int left, int right, int target) {
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
}
八、对比分析
| 方法 | 思路 | 优点 | 缺点 |
|---|---|---|---|
| 单次二分法 | 在一次查找中同时判断有序区间 | 简洁高效 | 分支逻辑稍复杂 |
| 找旋转点 + 二分 | 先定位旋转点再两次查找 | 思路清晰、结构化 | 要进行两次二分,代码稍长 |
通过二分的逻辑提升,搜索旋转排序数组可以在 O(log n) 时间内完成。
关键是判断当前哪一部分有序 ,再结合 target 的取值范围决定下一步的查找方向。
这道题是经典的二分查找变形题,非常适合掌握二分技巧与边界判断。