LeetCode | Search in Rotated Sorted Array(在旋转数组中查找)


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);
        }
    }
};

点赞