面试题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; }