循环有序数组中的二分查找 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);
			}

		}


};




你可能感兴趣的