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

 

参考资料:

posted @ 2018-08-06 21:10  朕蹲厕唱忐忑  阅读(148)  评论(0编辑  收藏  举报