题目描述:整数数组
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)
的算法解决此问题。示例 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提示:
1 <= nums.length <= 5000
-10^4 <= nums[i] <= 10^4
nums
中的每个值都 独一无二- 题目数据保证
nums
在预先未知的某个下标上进行了旋转-10^4 <= target <= 10^4
首先看到O(logn)应该想到的是二分查找,但是二分查找是在有序数组中进行的,题目给的旋转数组的特点是左半段和右半段分别有序,所以将数组从中间分开处理,一定有一边是有序的。
代码思路:
-
二分查找的循环:使用
while
循环进行二分查找,条件是left <= right
-
计算中间指针:
mid = (right + left) / 2
。这里简单地计算中间索引,没有使用(right - left) / 2
来防止整数溢出,这种方法在left
和right
都是正数时是安全的 -
判断哪一半是有序的:在判断左半段是否有序部分时,使用了
nums[left] <= nums[mid]
-
在有序的半段中进行查找:如果
mid
到right
是有序的,并且target
位于这个范围内,则更新right = mid - 1
。如果left
到mid
是有序的,并且target
位于这个范围内,则更新left = mid + 1
总的来说,这段代码是在二分查找的基础上进行了扩展和修改,以处理旋转有序数组这种特殊情况。它在每一步都尝试利用有序的部分来缩小查找范围,并且考虑了目标值可能存在的两种情况:在有序部分和在无序部分。这种方法使得在旋转有序数组中查找目标值仍然可以保持O(logn)的时间复杂度。
class Solution {
public int search(int[] nums, int target) {
if (nums.length == 1) {
if (target == nums[0]) return 0;
else return -1;
}
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = (right + left) / 2;
if (target == nums[mid]) return mid;
else if (nums[left] <= nums[mid]) {//加上= 因为如果mid要和left或者是right相等时 是和left相等 所以判断的时候要带上left==mid这个情况
if (target < nums[mid] && target >= nums[left]) right = mid - 1;//在mid左边有序范围
else left = mid + 1;//在mid右边
} else {
if (target > nums[mid] && target <= nums[right]) left = mid + 1;//在mid右边有序范围
else right = mid - 1;//在mid左边
}
}
return -1;
}
}