循环有序数组中的二分查找 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是否在右半部分里。

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

[cpp]  view plain  copy
  1. class Solution {  
  2. public:  
  3.   
  4. int search(int A[], int n, int target)  
  5. {  
  6.     if(n<=0)  
  7.         return -1;  
  8.     int left = 0, right = n-1;  
  9.     while(left<=right)  
  10.     {  
  11.         int mid = left + ((right-left)/2);  
  12.         if(A[mid] == target)  
  13.             return mid;  
  14.   
  15.         if(A[left] <= A[mid])//转折点在右半边  
  16.         {  
  17.             if(A[left] <= target && target < A[mid])  
  18.                 right = mid - 1;  
  19.             else  
  20.                 left = mid + 1;  
  21.         }  
  22.         else //转折点在左半边  
  23.         {  
  24.             if(A[mid] < target && target <= A[right])  
  25.                 left = mid + 1;  
  26.             else  
  27.                 right = mid - 1;  
  28.         }  
  29.     }  
  30.     return -1;  
  31. }  
  32.   
  33. };  


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

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

[cpp]  view plain  copy
  1. class Solution {  
  2.     public:  
  3.         bool search(int A[], int n, int target) {  
  4.             return bisearch(A, 0, n-1, target);  
  5.         }  
  6.   
  7.         bool bisearch(int A[], int left, int right, int target)  
  8.         {  
  9.             if(left > right)  
  10.                 return false;  
  11.             int mid = left + (right - left)/2;  
  12.             if(target == A[mid])  
  13.                 return true;  
  14.             // A[left], A[mid], A[right]  
  15.             // 1 3 5  
  16.             if(A[mid] > A[left] && A[mid] < A[right]) //普通二分  
  17.             {  
  18.                 if(target < A[mid])  
  19.                     return bisearch(A, left, mid-1, target);  
  20.                 else  
  21.                     return bisearch(A, mid+1, right, target);  
  22.             }  
  23.             // 5 1 3  
  24.             else if(A[mid] < A[left] && A[mid] < A[right]) //转折在左侧  
  25.             {  
  26.                 if(target > A[mid] && target <= A[right])  
  27.                     return bisearch(A, mid+1, right, target);  
  28.                 else  
  29.                     return bisearch(A, left, mid-1, target);  
  30.             }  
  31.             // 3 5 1  
  32.             else if(A[mid] > A[left] && A[mid] > A[right]) //转折在右侧  
  33.             {  
  34.                 if(target < A[mid] && target >= A[left])  
  35.                     return bisearch(A, left, mid-1, target);  
  36.                 else  
  37.                     return bisearch(A, mid+1, right, target);  
  38.             }  
  39.             // 3 3 3  
  40.             else //只有这种左中右都相等的情况下没有办法判断  
  41.             {  
  42.                 return bisearch(A, left, mid-1, target) || bisearch(A, mid+1, right, target);  
  43.             }  
  44.   
  45.         }  
  46.   
  47.   
  48. };  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值