要求
給定一個沒有重復元素的旋轉數組(它對應的原數組是有序的),求給定元素在旋轉數組內的下標(不存在的返回-1)。
例子
有序數組{0,1,2,3,4,5,6,7}對應的旋轉數組為{3,4,5,6,7,0,1,2}(左旋、右旋效果相同)。
- 查找元素5,返回結果2;
- 查找元素8,返回結果-1。
分析
可以輕易地想到遍歷一遍O(n)時間可以得到結果,但是並不是最好的結果;利用有序的特點,可以輕易的想到二分查找的方法。經過旋轉后的數組總是可以分為兩個有序序列,如下圖所示。旋轉數組分成了紅藍兩段有序序列。
可以看出中間位置m(m')的兩邊必然有一個是有序的,如果是m則左邊有序;如果是m'則右邊有序;如下可以看出。
總結規律:每次根據L和R求出m后,m左邊[L, m-1]和右邊[m+1, R]這兩部分中至少一個是有序的。
(1)arr[m]=X,返回m
(2)arr[m]<arr[R],位於m'位置右側是有序的;當arr[m']<X<arr[R]時,則L=m'+1,否則R=m'-1。
(3)arr[m]>=arr[R],位於m位置左側是有序的;當arr[L]<X<arr[m]時,則R=m-1,否則L=m+1。
參考代碼
數組的旋轉點擊這里查看。
int BSearch(int *arr, int len, int X){ int L = 0, R = len - 1; int m; while (L <= R){ //循環條件
m = (L + R) / 2; if (arr[m] == X) //找到了,終止
return m; if (arr[m] < arr[R]){ //右側有序
if (arr[m] < X && arr[R] >= X){ L = m + 1; } else R = m - 1; } else{ //左側有序
if (arr[m] > X && arr[L] <= X){ R = m - 1; } else L = m + 1; } } return -1; //循環結束,沒找到。
}
擴展
上面要求的是沒有重復元素,現在稍微擴展一下,可以有重復的元素,其它的要求不變。
思路:大致思路和原來相同,只不過相等的情況單獨處理。找到的元素位置,並不一定是第一次出現的位置。
- arr[L]<arr[m];左邊有序
- arr[L]>arr[m];右邊有序
- arr[L]=arr[m];相等,特殊處理,++L(L=m+1也可以)。
和arr[R]比較也可以。
int BSearch(int *arr, int len, int X){ int L = 0, R = len - 1; int m; while (L <= R){ //循環條件
m = (L + R) / 2; if (arr[m] == X) //找到了,終止
return m; if (arr[m] < arr[L]){ //右側有序
if (arr[m] < X && arr[R] >= X){ L = m + 1; } else R = m - 1; } else if (arr[m] > arr[L]){ //左側有序
if (arr[m] > X && arr[L] <= X){ R = m - 1; } else L = m + 1; } else //相等情況
L = m + 1; } return -1; //循環結束,沒找到。
}
2015-10-14 15:32:56
