循环有序数组中的二分查找 Search in a rotated sorted array


二分查找是必须要掌握的技能,适用于有序的、顺序的存储结构。

1、可以用它来查找某一个数

2、可以用于查找某一个范围。如《二分查找有序数组中某个数的所在范围 Search for a Range》。

3、可以用它来查找两个有序数组的中位数。

4、本文中,二分查找又多了一项新的本领,可以用它在循环有序数组中查找某个数。

二分查找的关键,在于求出中间下标mid之后,我能判断出key在mid的哪一侧,然后去那一侧找。

循环有序数组:

指的是,将一个有序数组循环左/右移动若干距离之后变成的数组。如,[1,2,3,4,5]循环右移3位,就成为[4,5,1,2,3]。该数组的特点是,其中包含着一个转折点。转折点左右两侧的子数组都是有序的,并且左侧的子数组整体都比右侧的子数组大。

查找方法:

不要想着直接定位到转折点。

只需要知道转折点在中间点的哪一侧就行。因为不含转折点的一侧一定是单调递增的,依然能够帮助我判断出key在mid的哪一侧。

根据left和right下标,求得mid下标。

如果A[left]<=A[mid],那么A[mid]一定不在转折点左侧,意味着从left到mid的整个左半部分都是严格递增的,因此我能够判断key是否在左半部分里。

如果A[left]>A[mid],那么A[mid]一点在转折点左侧,意味着从mid到right的整个右半部分是严格递增的,因此我能够判断key是否在右半部分里。

实现一(数组中不包含重复元素):


class Solution {
public:
 
int search(int A[], int n, int target)
{
    if(n<=0)
        return -1;
    int left = 0, right = n-1;
    while(left<=right)
    {
        int mid = left + ((right-left)/2);
        if(A[mid] == target)
            return mid;
 
        if(A[left] <= A[mid])//转折点在右半边
        {
            if(A[left] <= target && target < A[mid])
                right = mid - 1;
            else
                left = mid + 1;
        }
        else //转折点在左半边
        {
            if(A[mid] < target && target <= A[right])
                left = mid + 1;
            else
                right = mid - 1;
        }
    }
    return -1;
}
 
};


上面的方法没有考虑数组中有重复元素的情形。

实现二(数组中可能出现重复元素):


class Solution {
    public:
        bool search(int A[], int n, int target) {
            return bisearch(A, 0, n-1, target);
        }
 
        bool bisearch(int A[], int left, int right, int target)
        {
            if(left > right)
                return false;
            int mid = left + (right - left)/2;
            if(target == A[mid])
                return true;
            // A[left], A[mid], A[right]
            // 1 3 5
            if(A[mid] > A[left] && A[mid] < A[right]) //普通二分
            {
                if(target < A[mid])
                    return bisearch(A, left, mid-1, target);
                else
                    return bisearch(A, mid+1, right, target);
            }
            // 5 1 3
            else if(A[mid] < A[left] && A[mid] < A[right]) //转折在左侧
            {
                if(target > A[mid] && target <= A[right])
                    return bisearch(A, mid+1, right, target);
                else
                    return bisearch(A, left, mid-1, target);
            }
            // 3 5 1
            else if(A[mid] > A[left] && A[mid] > A[right]) //转折在右侧
            {
                if(target < A[mid] && target >= A[left])
                    return bisearch(A, left, mid-1, target);
                else
                    return bisearch(A, mid+1, right, target);
            }
            // 3 3 3
            else //只有这种左中右都相等的情况下没有办法判断
            {
                return bisearch(A, left, mid-1, target) || bisearch(A, mid+1, right, target);
            }
 
        }
 
 
};

发布了17 篇原创文章 · 获赞 2 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/u011250186/article/details/103865170