第六题 旋转数组的最小数字
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
看到这题目时,我觉得“非减排序”说的有点含糊,个人觉得这包含很多种情况,
例如,[1,1,1,1,1] 、[1,2,3,4,5] 、[1,2,3,3,3,4,5] 、[1,2,3,2,3,2,1] 等都是非减的情况
在牛客网上也看到了很多人在讨论关于这个问题,所以我翻了一下《剑指offer》原书,看一下作者的原意想考什么,书上题目是输入“递增排序”的数组,考察二分法查找,所以我在做这题的时候也忽略了其他情况,只考虑了“递增排序”,书上还指出最开始的若干个元素搬到数组的末尾,有特例:把前面0个元素搬到最后面,即排序数组本身,这仍是数组的一个旋转。
做这题的时候用了两种方法
方法一:
分析:
旋转后的数组可以划分为两个排序的子数组,前面的子数组元素大于或等于后面子数组的元素,为了便于分辨,本文将前面子数组称为一号数组,后面的子数组称为二号数组。那么最小元素就是这两个数组的分界点
1、空数组,直接输出0
2、循环找分界点rotateArray[i]>rotateArray[i+1] 前一个元素大于后一个元素
3、如果上一步没找到,说明旋转了0个元素,直接输出第0个元素
所以,当找到一个数小于前一个,那么这就是最小的元素
function minNumberInRotateArray(rotateArray)
{
// write code here
var len=rotateArray.length
if(!len)return 0 ;//数组为空,返回0
for(var i=0;i<len-1;i++){
if(rotateArray[i]>rotateArray[i+1]){ //前一个元素大于后一个元素时,找到最小元素
return rotateArray[i+1]
}
}
//如果找不到前一个元素大于后一个元素,说明旋转了0个元素,那么第一个元素就是最小的元素,直接输出
return rotateArray[0]
}
时间复杂度为O(n)
方法二:
分析:
一、用三个指针,指向第一个元素(left)、最后一个元素(right)、中间元素(mid)
二、如果第一个元素小于最后一个元素,则说明旋转了0个元素,直接输出第一个元素
三、如果第一个元素>=最后一个元素,则说明旋转了n个元素(n>0)
不管怎么移动,left指针一直指向一号数组;right指针一直指向二号数组;
1、如果mid指向的元素>left指向的元素 => mid指向的元素是 一号数组 的,最小元素在后面,所以让left过来指向mid的元素(缩小寻找范围)
2、如果mid指向的元素<left指向的元素 => mid指向的元素是 二号数组 的,
所以让right过来指向mid的元素(缩小寻找范围)
如果left和right相邻了,那么right此时指向的元素就是最小元素
3、如果mid、left、right指向的元素都相等了,mid此时无法判断身处在哪个数组,即无法判断最小元素在哪边,所以无法判断往哪边移动缩小范围。只能顺序遍历寻找
还是上图解释吧 图解:
通过二分法查找,左右指针不断接近最小元素,保证right指向最小元素,然后输出。也参考了一下书上代码
function minNumberInRotateArray(rotateArray)
{
// write code here
var left=0, //指向第一个元素指针
right=rotateArray.length-1 //指向最后一个元素指针
mid=0; //指向中间一个元素指针
if(right==-1)return 0; //数组为空时,返回0
if(right==left)return rotateArray[0]; //若数组只有一个数时,直接返回
while(rotateArray[left]>=rotateArray[right]){
//如果两个指针相邻,说明已经找到,right指针指向的就是最小元素
if(right-1==left)return rotateArray[right]
mid=parseInt((left+right)/2); //如果元素个数是偶数个,则直接取整数
//如果左右指针和中间指针指向的元素都相等时,调用顺序查找函数
if(rotateArray[mid]==rotateArray[right]&&rotateArray[left]==rotateArray[right]){
return findMin(rotateArray,left,right)
}
if(rotateArray[mid]>=rotateArray[right]){//如果mid指针指向的元素大于或等于left指针指向的元素,说明mid在一号数组,那么最小元素在mid右边,则把left指针指向中间元素,进入下一个循环
left=mid;
}else if(rotateArray[mid]<=rotateArray[right]){//如果mid指针小于或等于right指针指向的元素,说明mid在二号数组,那么最小元素在mid左边,则把right指针指向中间元素,进入下一个循环
right=mid;
}
}
//旋转了0个元素时,排序就是数组本身,直接返回第一个就是最小的元素
return rotateArray[0]
}
//顺序查找函数
function findMin(arr,left,right){
var index=arr[left];
for(var i=left+1;i<=right;i++){
if(index>arr[i]) {
index=arr[i]
}
return arr[i]
}
}
时间复杂度为O(logn)
关键点 :分界点 二分法
知识点:查找 数组
如果有哪些不正确的地方请指出~