Suppose a sorted array 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.
题目解析:
方案一:暴力方法,一个一个比较,最后找到坐标位置。O(n)时间复杂度,最不高效的算法。
方案二:利用二分查找,但是这个题目是分块有序的,于是想到了一个方案,找到分割点,然后再根据值所在的范围,选其中一个区域进行二分搜索。但是实现中遇到了不小的麻烦,本身二分查找的代码就很巧,什么时候要加等号,什么时候不加等号,当涉及到在二分查找上加条件的时候,就更容易出错,网上有个代码方案:找到最小值的位置:
利用二分查找找到转折点,也就是最小值的位置,当a[left]<=a[mid]时,那么在left…mid之间是有序的,a[left]为最小值,或者在mid+1…right之间取最小值,最后取两者最小。
对于a[left]>a[mid]也有类似的思想。随后再利用二分查找来查询位置。
class Solution {
public:
int findPos(int a[], int left, int right)
{
if (left > right)
return -1;
int mid = left + (right - left) / 2;
if (a[left] <= a[mid])
{
int pos = findPos(a, mid + 1, right);
if (pos == -1)
return left;
else
return a[left] < a[pos] ? left : pos;
}
else
{
int pos = findPos(a, left, mid - 1);
if (pos == -1)
return mid;
else
return a[pos] < a[mid] ? pos : mid;
}
}
int bsearch(int a[], int left, int right, int key)
{
if (left > right)
return -1;
int mid = left + (right - left) / 2;
if (a[mid] == key)
return mid;
else if (a[mid] < key)
return bsearch(a, mid + 1, right, key);
else
return bsearch(a, left, mid - 1, key);
}
int search(int A[], int n, int target) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
int pos = findPos(A, 0, n - 1);
int index = bsearch(A, 0, pos - 1, target);
if (index != -1)
return index;
return bsearch(A, pos, n - 1, target);
}
};
整个的思想是很正常的,但是实现起来也有不小的难度,各种细节需要注意。
方案三:
分析方法,代码中分割情况进行了变换,当没查询到值时,不再是A[mid] 与 target之间的比较了,而是换成了A[mid] > A[right]的关系比较,在这种情况下,还合并了多种可能。通过深入分析情况得出的结果代码。
思路很简单:
当a[left] <= a[mid]的时候,这段区域是有序的,判断target是否在这个区域中,是的话,就更改right。
当a[left]>a[mid]的时候,mid…right区域是有序的,判断target和a[mid] a[right]的关系就能确定是否在这个区域中
class Solution {
public:
int search(int A[], int n, int target) {
if(NULL == A || 0 == n)
return -1;
int left = 0, right = n-1, mid = 0;
while(left <= right)
{
mid = left+(right-left)/2;
if(A[mid] == target)
return mid;
else if(A[left] <= A[mid])//这里比较的时候,不再是以中间值与target之间的比较,而是区域比较。
{
if(A[left] <= target && target < A[mid]) //将各种情况合并
right = mid - 1;
else
left = mid + 1;
}
else//right
{
if(A[mid] < target && target <= A[right])
left = mid + 1;
else
right = mid - 1;
}
}
return -1;
}
};
方案四:
递归的思想,要准确的掌握好!我们碰到的二分查找,都是利用非递归来求解的,如果我们碰到在二分查找基础上增强条件时,用非递归会出现很多意外情况。但是,利用递归的话,思路会很简单!
利用mid判断与a[lo]的关系后,如果是有序的,那么根据target与该区间的范围,就可以知道应在哪段区域之间进行递归了!思路很简单,代码也很容易写出。
class Solution {
public:
int search(int A[], int n, int target) {
if(n<=0)
return -1;
return BinarySearch(A,0,n-1,target);
}
int BinarySearch(int A[],int lo,int hi,int target){
if(lo>hi)
return -1;
int mid = (lo + hi)/2;
if(A[mid] == target)
return mid;
if(A[lo]<=A[mid]){ //这里要加等号,避免mid和lo相等时的情况。
if(target>=A[lo] && target <= A[mid]) //找寻的结果在lo...mid的有序区间,进行递归求解
return BinarySearch(A,lo,mid-1,target);
else //找寻的结果在另一半区间,进行递归
return BinarySearch(A,mid+1,hi,target);
}else{
if(target>A[mid] && target <= A[hi])
return BinarySearch(A,mid+1,hi,target);
else
return BinarySearch(A,lo,mid-1,target);
}
}
};