面试题11:旋转数组的最小数字
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转旋转数组的最小元素。例如数组{3,4,5,1,2}是数组{1,2,3,4,5}的一个旋转,该数组的最小值为1。
思路:
-
方法一:通过顺序查找法可以在O(n)的时间里找到最小的数字。根据该数组的特点,肯定不是最优解;
-
方法二:通过二分查找法查找。和一般的有序数组不一样,该数组通过旋转操作将该数组分为了两个子数组,而且两个子数组都是有序的。根据该特点,我们可以给前后两个子数组分配一个指针。查找过程:
1 .第一个指针p1指向第一个元素,第二个指针p2指向最后一个元素;
2 .求两指针的中间位置,若中间位置到值大于第一个指针所指,则说明中间位置在第一个子数组中,则更新第一个指针;若中间位置到值小于第二个指针所指,则说明中间位置在第二个子数组中,则更新第二个指针
3 .更新完指针后,若两指针相邻,则返回第二个指针所指的值,否者进行2步。特殊情况:第2步中,若第一个指针和第二个指针以及中间的值相等,则不能再用二分查找,改为顺序查找
代码:
#include <iostream>
using namespace std;
int minInRotateArr(int rotateArr[], int length);
int main() {
int rotateArr[] = { 4,5,6,7,8,2,2,3 };
int min = minInRotateArr(rotateArr, 8);
cout << "min:" << min << endl;
return 0;
}
int minInRotateArr(int rotateArr[], int length) {
if (rotateArr == nullptr || length <= 0) {
return -1;
}
int p1 = 0;//指向前一个子数列的指针
int p2 = length - 1;//指向后一个子数列的指针
int mid = p1;
while (rotateArr[p1] >= rotateArr[p2]) {
//结束条件:前一个子数列的指针和后一个子数列指针相邻,那么最小的值就位于后一个指针所指出
if (p2 - p1 == 1) {
return rotateArr[p2];
}
mid = (p1 + p2) / 2;
//如果中间的值和两端一样,只能顺序查找
if (rotateArr[p1] == rotateArr[p2] && rotateArr[p1] == rotateArr[mid]) {
int min = rotateArr[p1];
for (int i = p1 + 1; i <= p2; i++) {
if (min > rotateArr[i]) min = rotateArr[i];
}
return min;
}
//如果中间的值比前一个指针指向的值大,说明mid所处前一个子数列
if (rotateArr[mid] >= rotateArr[p1]) {
p1 = mid;
}
//如果中间的值比后一个指针指向的值小,说明mid所处前一个子数列
else if (rotateArr[mid] <= rotateArr[p2]) {
p2 = mid;
}
}
return rotateArr[mid];
}