Description
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7
might become 4 5 6 7 0 1 2
).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
Solution
Binary search, time O(logn), space O(1)
首先注意这道题跟常规的binary search不一样,如果找不到就要返回-1。所以要在while loop中直接对nums[mid] == target的情况进行返回。
另外这道题比较tricky的点是条件的选择。首先我们排除掉nums[mid] == target的情况,然后mid将数组分成了两个区间:[left, mid)和(mid, right],可以判断nums[mid]和nums[right]的大小关系,找到上面两个区间里面有序的那个,然后判断target是否在有序区间里,再缩小范围就行了。
class Solution {
public int search(int[] nums, int target) {
if (nums == null || nums.length < 1) {
return -1;
}
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < nums[right]) {
if (target > nums[mid] && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
} else if (nums[mid] > nums[right]) {
if (target >= nums[left] && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else { // left == mid == right and nums[mid] != target
break;
}
}
return -1;
}
}
另一种做法也挺巧妙:
Let's say nums looks like this: [12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Because it's not fully sorted, we can't do normal binary search. But here comes the trick:
- If target is let's say 14, then we adjust nums to this, where "inf" means infinity:
[12, 13, 14, 15, 16, 17, 18, 19, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf] - If target is let's say 7, then we adjust nums to this:
[-inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
And then we can simply do ordinary binary search.
Of course we don't actually adjust the whole array but instead adjust only on the fly only the elements we look at. And the adjustment is done by comparing both the target and the actual element against nums[0]
.
class Solution {
public int search(int[] nums, int target) {
if (nums == null || nums.length < 1) {
return -1;
}
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
int val = (nums[mid] >= nums[0]) == (target >= nums[0]) // tricky: >= instead of >
? nums[mid] : (target >= nums[0] ? Integer.MAX_VALUE : Integer.MIN_VALUE);
if (target < val) {
right = mid - 1;
} else if (target > val) {
left = mid + 1;
} else {
return mid;
}
}
return -1;
}
}