在旋转有序数组中查找元素
1. 题目:给定一个旋转的有序数组,比如{7,8,9,10,1,2,3}是{1,2,3,7,8,9,10}旋转之后得到的,在数组中查找是否存在元素key。要求时间复杂度为O(lgn)。假定数组中不存在重复元素。
2. 分析:从上面的选择数组可以发现,array[middle]将数组分成两段,两段中必有一段是有序的。这样就可以使用二分查找了。一个变形的二分查找。
3. 代码:
1 #include <iostream> 2 #include <cassert> 3 4 using namespace std; 5 6 int rotateBinarySearch(int *a,int aLength,int key) 7 { 8 int low=0; 9 int high=aLength-1; 10 while (low<=high) 11 { 12 int middle=low+((high-low)>>1); 13 if (a[middle]==key) 14 { 15 return middle; 16 } 17 //低端有序 18 if (a[low]<=a[middle]) 19 { 20 if (key>=a[low] && key<a[middle]) 21 { 22 high=middle-1; 23 } 24 else 25 { 26 low=middle+1; 27 } 28 } 29 //高端有序 30 else 31 { 32 if (key>a[middle] && key<=a[high]) 33 { 34 low=middle+1; 35 } 36 else 37 { 38 high=middle-1; 39 } 40 } 41 } 42 return -1; 43 } 44 int main() 45 { 46 enum{aLength=8}; 47 int a[aLength]={4,5,6,7,8,1,2,3}; 48 for (int i=0;i<aLength;i++) 49 { 50 cout<<rotateBinarySearch(a,aLength,a[i])<<" "; 51 } 52 cout<<rotateBinarySearch(a,aLength,11); 53 cout<<endl; 54 return 0; 55 }
4. 一个变形题目:给定一个旋转有序数组,这个数组是通过下面的方式得到的:给定一个有序数组(从小到大)和一个数X(0<=X<=arrayLength-1),数组中的第i位置的元素变到a[(i+X)%arrayLength]位置。求出X的值。
分析:原先数组中最小的数也就是下标为0的数经过旋转后变到的位置为a[X],所以只要找到这个最小的数,其所在位置就是X。
代码:
1 #include <iostream> 2 #include <cassert> 3 4 using namespace std; 5 6 int findX(int *a,int aLength) 7 { 8 int low=0; 9 int high=aLength-1; 10 while (a[low]>a[high]) 11 { 12 int middle=low+((high-low)>>1); 13 if (a[middle]>a[high]) 14 { 15 low=middle+1; 16 } 17 else 18 high=middle; 19 } 20 return low; 21 } 22 void testFindX(int *a,int aLength) 23 { 24 int *b=new int[aLength]; 25 for (int i=0;i<aLength;i++) 26 { 27 for (int j=0;j<aLength;j++) 28 { 29 b[(j+i)%aLength]=a[j]; 30 } 31 cout<<findX(b,aLength)<<" "; 32 } 33 cout<<endl; 34 delete[] b; 35 } 36 int main() 37 { 38 enum{aLength=8}; 39 int a[aLength]={1,2,3,4,5,6,7,8}; 40 testFindX(a,aLength); 41 return 0; 42 }
注意点:注意这次二分查找的循环条件,并使用low记录最小元素的位置,返回low。
5. 在上面的testFindX中数组旋转都是使用新的数组来存储。如果要求原地旋转怎么办?这个就是一个三次逆转的过程了:比如{a,b,c,d,e,f,g}经过k=3的旋转变为{e,f,g,a,b,c,d},可以通过三次逆转实现,第一步将a到a+k逆转,即将{a,b,c,d}逆转为{d,c,b,a};第二步将a+k+1到a+aLength-1逆转,即将{e,f,g}变为{g,f,e}。
此时的数组已变成{d,c,b,a,g,f,e};第三步讲上面逆转之后的数组整体逆转变为{e,f,g,a,b,c,d}。经过上面的三步就实现了数组的原地旋转。
6. 参考文章:
http://www.leetcode.com/2010/04/searching-element-in-rotated-array.html
http://www.leetcode.com/2010/04/rotating-array-in-place.html