在旋转有序数组中查找元素

1. 题目:给定一个旋转的有序数组,比如{7,8,9,10,1,2,3}是{1,2,3,7,8,9,10}旋转之后得到的,在数组中查找是否存在元素key。要求时间复杂度为O(lgn)。假定数组中不存在重复元素。

2. 分析:从上面的选择数组可以发现,array[middle]将数组分成两段,两段中必有一段是有序的。这样就可以使用二分查找了。一个变形的二分查找。

3. 代码:

View Code
 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。

  代码:

View Code
 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

posted @ 2012-08-10 14:52  kasuosuo  阅读(1283)  评论(0编辑  收藏  举报