剑指offer面试题八:旋转数组的最小数字

一,题目

  把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。

二,分析

  最容易想到的方法是从头到尾遍历一遍,时间复杂度为O(n)。

  但是我们并没有利用旋转数组本身的性质,发现原数组可以划分为两个排序的子数组,而且前面的子数组元素都大于或等于后面的子数组。而且我们要找的最小元素正好是这两个子数组的分界线。在排序的数组中我们可以用二分查找实现O(logn)查找。所以我们可以试着用二分查找的思路来寻找最小的元素。

  我们举一个简单的例子,如数组{3,4,5,1,2}是排序数组{1,2,3,4,5}的旋转数组。用两个指针分别指向第一个元素和第5个元素。位于两个指针中间的数字是5,它大于第一个指针指向的数字,因此中间数字5一定位于第一个递增子数组中,并且最小的数字一定位于它的后面。因此我们可以移动第一个指针让它指向数组的中间。

  此时位于两个指针中间的数字是1,它小于第二个指针指向的数字,因此中间数字1一定位于第二个递增子数组中,并且最小的数字一定位于它的前面或者本身就是最小数字。

  以此类推,当两个指针的距离是1,表明第一个指针已经指向了第一个递增子数组的末尾,而第二个指针指向第二个递增子数组的开头,也就是最小的数字。

三,实现

  基于上述分析,可以写出如下代码:

  

#include "stdafx.h"
#include<iostream>
using namespace std;

int MinInorder(int* numbers, int index1, int index2)
{
	int result = numbers[index1];
	for(int i = index1 + 1; i <= index2; ++i)
	{
		if(result > numbers[i])
			result = numbers[i];
	}
	return result;
}

int Min(int *numbers, int length)
{
	if(numbers == NULL || length <= 0)
	{
		cout<<"invalid parameters"<<endl;
		return NULL;
	}
	int index1 = 0, index2 = length - 1;
	int indexMid = index1;
	while(numbers[index1] >= numbers[index2])
	{
		if(index2 - index1 == 1)//循环结束条件
		{
			indexMid = index2;
			break;
		}
		indexMid = (index1 + index2)/2;
		if(numbers[index1] == numbers[index2] && numbers[index1] == numbers[indexMid])
			return MinInorder(numbers, index1, index2);
		else if(numbers[indexMid] >= numbers[index1])
			index1 = indexMid;
		else if(numbers[index2] >= numbers[indexMid])
			index2 = indexMid;
	}
	return numbers[indexMid];
}

int main()
{
	int a;
	//int data[5] = {3,4,5,1,2};//一般旋转数组
	//int data[] = {1,0,1,1,1};//特殊情况
	//int data[] = {1,1,1,0,1};
	//int data[] = {6,6,7,5,5};
	a = Min(data,5);
	cout<<a<<endl;
	system("pause");
	return 0;
}

  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值