问题描述:假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
例:输入: nums = [4,5,6,7,0,1,2], target = 0;输出: 4
注:
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别
思路:
- 二分查找
由于为升序数组旋转后形成,利用此结构,分析所有情况
令left指向查找序列最左元素,right为最右,mid为中间元素
- 当nums[left] < nums[right]时,此时未旋转,有两种情况。
- nums[left] ≤ target ≤ nums[mid],向前寻找
- nums[mid] ≤ target ≤ nums[right],向后寻找
- 当nums[left] > nums[right]时,发生旋转,有四种情况。
- nums[left] > nums[mid],旋转在前
- nums[mid] ≤ target ≤ nums[right],向后寻找
- target ≤ nums[mid] or target ≥ nums[left],向前寻找
- nums[left] < nums[mid],旋转在后
- nums[left] ≤ target ≤ nums[mid],向前寻找
- nums[mid] ≤ target or nums[left] ≥ target,向后寻找
总结来看,向前寻找共有三条,其余情况为向后寻找
- nums[left] ≤ target ≤ nums[mid]
- -----------------target ≤ nums[mid] < nums[left]
- ----------------------------nums[mid] < nums[left] ≤ target
由此可得出三条判断条件:nums[left] < target;target < nums[mid];nums[mid] < nums[left],那么上述向前查找的情况对应任意两条判断情况成立。即若上述条件两条成立,向前查找,否则向后查找。(三条件包含所有情况,故不可能同时为假)
于是判断可转化为异或操作(1 ^ 2 ^ 3)(两条成立时为假,其余为真),进行边界的移动判断。
- 二次查找
采用两次二分查找,第一次找到偏移次数,第二次基于偏移利用常规二分查找出给定元素。
//java
//二分查找
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = (left+right) / 2;
if(nums[mid] == target) return mid;
if((target >= nums[left]) ^ (nums[left] > nums[mid]) ^ (nums[mid] >= target))
left = mid + 1;
else
right = mid - 1;
}
return -1;
}
}
//二次二分
class Solution {
public int search(int[] nums, int target) {
int len = nums.length;
if(len == 0) return -1;
int x = delta(nums, 0, len-1);
int i = 0;
int j = len-1;
while(i <= j){
int mid = (i+j) / 2;
int tmid = (mid + x)%len;//基于未移动数组的位置
if(nums[tmid] == target)
return tmid;
else if(nums[tmid] > target)
j = mid - 1;
else
i = mid + 1;
}
return -1;
}
public int delta(int[] nums, int left, int right){//找到移位次数
int mid = (left + right) / 2;
if(nums[mid] > nums[(mid+1)%nums.length])//一旦该数下一元素更大,找到原数组末尾元素
return (mid+1)%nums.length;
else if(nums[mid] < nums[right])
return delta(nums, left, mid);
else if(nums[mid] > nums[right])
return delta(nums, mid, right);
else
return 0;
}
}