JS《剑指offer》第六题 旋转数组的最小数字

第六题 旋转数组的最小数字

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{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)

关键点 :分界点 二分法
知识点:查找 数组

如果有哪些不正确的地方请指出~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值