面试题11:旋转数组的最小数字
// 面试题11:旋转数组的最小数字
// 题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
// 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组
// {3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。
解题思路:
最简单的方法就是从头到尾遍历,遍历完成后就可以找出最小的元素,但是这样的时间效率是O(n)。
而且这样的算法效率很低,毫无技术可言。
旋转数组其实就是两个排好序的升序数组,前边数组元素都是>后边数组元素的,可以采用二分查找的思想。
两个指针p1,p2分别指向数组的头部和尾部。
p1 p2中间元素如果>=p1,说明中间元素位于前面的升序数组中,将其赋给p1
p1 p2中间元素如果<=p2,说明中间元素位于后面的升序数组中,将其赋给p2
重复这个过程,直到p1 p2距离为1,p2指向的就是我们要找的最小元素。
有几种特殊情况需要考虑到:
1)数组最开始的0的元素被搬运到了数组的末尾,也就是{1, 2, 3, 4, 5}旋转后还是{1, 2, 3, 4, 5}。
这种情况可以在最开始判断,如果p1指向的元素<p2指向的元素,直接返回p1
2)p1和p2指向的元素,以及中间元素相等,无法判断中间元素是属于前后那个数组,也无法移动指针来缩小范围,
这个时候我们只能使用最原始的顺序查找来寻找最小值。
伪代码:
if(参数输入无效)
throw exception;
p1p2分别指向数组首尾元素;
pMiddle=p1;
while(p1元素>=p2元素){
if(p1p2相邻)
return p2;
重新计算pMiddle;
if(pMiddle元素>=p1元素)
p1=pMiddle;
else if(pMiddle元素<=p2元素)
p2=pMiddle;
}
return pMiddle;
c/c++:
int Min(int* numbers, int length) {
//检验参数有效性
if (numbers == nullptr || length <= 0)
throw new std::exception("Invalid parameters.");
int pIndex = 0;
int pEnd = length - 1;
int pMiddle = pIndex;
//旋转元素数量不为0,开始二分查找
while (numbers[pIndex] >= numbers[pEnd]) {
pMiddle = (pIndex + pEnd) / 2;
//两个指针相邻,找到最小元素
if (pEnd - pIndex == 1) {
pMiddle = pEnd;
break;
}
//首尾中元素大小相等,无法缩小范围
//开始顺序查找
if (numbers[pIndex] == numbers[pEnd]
&& numbers[pIndex] == numbers[pMiddle])
return MinOrder(numbers, pIndex, pEnd);
//根据中间元素与首尾元素大小
//缩小查找范围
if (numbers[pMiddle] >= numbers[pIndex])
pIndex = pMiddle;
else if (numbers[pMiddle] <= numbers[pEnd])
pEnd = pMiddle;
}
return numbers[pMiddle];
}
int MinOrder(int* numbers, int index, int end) {
int result = numbers[index];
for (int i = index + 1; i <= end; i++) {
if (numbers[i] < result)
result = numbers[i];
}
return result;
}