题意: 假设有一个排好序的数组,在某个你不知道的地方进行了旋转。给定一个需要查找的目标值,如果能找到就返回它的索引,否则返回-1。该数组中没有重复的值。
举例
数组(0, 1, 2, 4, 5, 6, 7)旋转后变成(4, 5, 6, 7, 0, 1, 2)。
查找5,返回1;
查找1,返回5;
查找3,返回-1;
分析: 首先最简单的,遍历一趟整个数组,查找目标值,时间复杂度为O(n)。那么有没有效率更高的呢?答案肯定是有的,那就是二分搜索。解决该问题需要两次二分搜索,第一次二分用来找到那个旋转点,比如例子中的那个’0’在数组中的位置4即为旋转点,用来判断是否为旋转点的条件是index位置的值大于index + 1位置的值(index + 1是旋转点)。第二次二分用于查找目标值,首先将该数组根据旋转点划分成两个,如上例中可以按位置划分成[0, …, 3]和 [4, …, 6]两个,判断目标值在其中的哪个区间,在该区间内二分查找即可。
代码
public int search(int[] A, int target) {
int pivot_index = searchPivot(A, 0, A.length - 1); //获得旋转点所在位置
if(target >= A[pivot_index] && target <= A[A.length - 1]){//查找target
return binarySearch(A, pivot_index, A.length - 1, target);
}else if(pivot_index > 0 && target >= A[0] && target <= A[pivot_index - 1]){
return binarySearch(A, 0, pivot_index - 1, target);
}else{
return -1;
}
}
/**
* 二分找到旋转点,如果没有就返回0
*/
public int searchPivot(int[] A, int begin, int end){
if(begin > end) return 0; //如果找不到pivot,则表示该数组没有旋转
else{
int mid = (begin + end) / 2;
if(mid < A.length - 1 && A[mid] > A[mid + 1]){
return mid + 1;
}else{
int left = searchPivot(A, begin, mid - 1); //分治找左边
int right = searchPivot(A, mid + 1, end); //分治找右边
if(left != 0){
return left;
}else if(right != 0){
return right;
}else{
return 0;
}
}
}
}
/**
* 二分查找target
*/
public int binarySearch(int[]A, int begin, int end, int target){
if(begin > end) return -1;
else{
int mid = (begin + end) /2;
if(A[mid] == target) return mid;
else{
if(A[mid] > target) return binarySearch(A, begin, mid - 1, target);
else return binarySearch(A, mid + 1, end, target);
}
}
}