旋转有序数组的二分查找
要求
给定一个没有重复元素的旋转数组(它对应的原数组是有序的),求给定元素在旋转数组内的下标(不存在的返回-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