33_搜索旋转排序数组[MEDIUM]
约 829 字大约 3 分钟
2026-02-26
题目:search-in-rotated-sorted-array
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k上进行了 向左旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 下标 3 上向左旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1示例 3:
输入:nums = [1], target = 0
输出:-1解题思路
解法一
两次二分。第一次二分找旋转点(就是找最小值,见 leetcode 153题)
第二次在目标区间正常二分:
- 旋转点将数组分为两个升序部分:
[0...pivot-1]和[pivot...n-1]。 - 由于数组是旋转过的,右半段的所有元素一定小于或等于左半段的最大值。
- 我们只需看 target 是否在右半段的数值范围内(即
nums[pivot] <= target <= nums[n-1]),就能决定去哪一边搜。
解法二
一次二分。从中间砍一刀,左右两边必然有一边是排序的,如果目标值在排序的那一边,直接对那一边正常二分。否则,对乱序的那一边,中间再砍一刀,继续上述流程。
Java 实现
解法一
class Solution {
public int search(int[] nums, int target) {
if (nums == null || nums.length == 0) return -1;
// 1. 找到旋转点(最小值)的下标
int pivot = findPivot(nums);
// 2. 确定 target 落在哪个有序区间
int n = nums.length;
if (target >= nums[pivot] && target <= nums[n - 1]) {
// 在右半段 [pivot, n-1] 查找
return binarySearch(nums, pivot, n - 1, target);
} else {
// 在左半段 [0, pivot-1] 查找
return binarySearch(nums, 0, pivot - 1, target);
}
}
// 第一步:寻找最小值(旋转点)的下标
private int findPivot(int[] nums) {
int left = 0, right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] > nums[right]) {
// mid 在左侧高位区,旋转点在 mid 右侧
left = mid + 1;
} else {
// mid 可能就是旋转点,或者在右侧低位区
right = mid;
}
}
return left;
}
// 第二步:标准二分查找
private int binarySearch(int[] nums, int left, int right, int target) {
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
}解法二
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right){
int mid = left + (right-left)/2;
if (target == nums[mid]){
return mid;
}
// 右边有序,看目标是否在右边区间内
if (nums[mid] <= nums[right]){
if (target > nums[mid] && target <= nums[right]){
left = mid + 1;
} else {
right = mid - 1;
}
} else { // 左边有序,看目标是否在左边区间内
if(target < nums[mid] && target >= nums[left]){
right = mid - 1;
} else {
left = mid + 1;
}
}
}
return -1;
}
}复杂度分析:
- 时间复杂度:O(logn)。每次迭代搜索范围都缩小一半,符合题目要求。
- 空间复杂度:O(1)。只需要常数级别的额外指针空间。
