目录
一、问题描述
整数数组 nums
按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums
在预先未知的某个下标 k
(0 <= k < nums.length
)上进行了 旋转 ,使数组变为 [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)
的算法解决此问题。
二、解题思路
我们需要通过修改标准二分查找的算法来解决旋转数组的问题。具体步骤如下:
- 取数组的中点
mid
,将数组分为左右两部分。因为数组是旋转的,所以至少一部分(左半部分或右半部分)是有序的。 - 检查目标值
target
是否在有序部分。如果是,则可以在这部分继续进行二分查找;如果不在,则去另一部分查找。 - 继续重复二分查找,直到找到目标值或确定目标值不存在。
详细步骤:
- 初始化
left
和right
指针分别指向数组的左右两端。 - 在
left <= right
的条件下进行循环:- 计算中点
mid
:mid = left + (right - left) / 2
。 - 如果
nums[mid] == target
,直接返回mid
。 - 判断哪一半是有序的:
- 如果
nums[left] <= nums[mid]
,说明左半部分是有序的。- 判断
target
是否在左半部分:如果nums[left] <= target < nums[mid]
,则在左半部分查找,更新right = mid - 1
;否则去右半部分查找,更新left = mid + 1
。
- 判断
- 如果
nums[mid] <= nums[right]
,说明右半部分是有序的。- 判断
target
是否在右半部分:如果nums[mid] < target <= nums[right]
,则在右半部分查找,更新left = mid + 1
;否则去左半部分查找,更新right = mid - 1
。
- 判断
- 如果
- 计算中点
- 如果退出循环时还没有找到目标值,返回 -1。
三、代码
java
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 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;
}
}
}
// 如果没有找到目标值,返回 -1
return -1;
}
}
四、复杂度分析
- 时间复杂度:由于每次将搜索空间缩小一半,因此时间复杂度是 O(logn)O(\log n)O(logn),符合题目要求。
- 空间复杂度:我们使用的是常数级别的额外空间,空间复杂度为 O(1)O(1)O(1)。