小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
原题:33. 搜索旋转排序数组
题目大意就是在一个经过了旋转的没有重复元素升序数组中查找目标元素的下标,上述的旋转操作就是将一个 [0,1,2,4,5,6,7] 这样的数组,旋转成为 [4,5,6,7,0,1,2]。
解题思路:
如果数组没有经过题目的旋转操作,可以很简单地通过二分查找的方法来查找目标元素的下标。我们可以自此基础上分析解题思路。经过「旋转」之后,虽然新的数组不再是一个完全的升序数组,但是它仍然可以在某种意义上说是有序的,它由两个升序数组构成,且右半部分升序数组的最大元素要小于左边生序数组的最小元素,也就是整个数组第一个元素大于最后一个元素。
基于此,这道题依然可以使用二分查找的方法来查找目标元素的下标,只是条件判断要复杂一些。
class Solution {
public int search(int[] nums, int target) {
int n = nums.length;
int l = 0, r = n - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (target == nums[mid]) {
return mid;
}
if (nums[0] <= nums[mid]) {
// 中间指针落在了左半部分的升序数列上
} else {
// 中间指针落在了右半部分的升序数列上
}
}
return -1;
}
}
这两种情况下,还需要做判断:
- 当满足
nums[0] <= nums[mid],也就是中间指针落在左半部分的升序数列上,那么,如果目标值的大小在nums[l]和nums[mid]之间,则目标值(如果存在)的下标在 mid 左侧,则将 r 指针移动到mid - 1的位置。否则,将 l 指针移动到mid + 1的位置。 - 如果不满足以上条件,则代表中间落在了右半部分的升叙述裂伤,此时则需要判断,如果目标值的大小在
nums[mid]和nums[r]之间,则目标值(如果存在)的下标在 mid 右侧,将 l 指针移动到mid + 1的位置。否则,将 r 指针移动到mid - 1的位置。
最终代码:
class Solution {
public int search(int[] nums, int target) {
int n = nums.length;
int l = 0, r = n - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (target == nums[mid]) {
return mid;
}
if (nums[0] <= nums[mid]) {
if (nums[l] <= target && target < nums[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[r]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return -1;
}
}