二分查找是必须要掌握的技能,适用于有序的、顺序的存储结构。
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);
}
}
};