使用二分法解决旋转数组的最小数字的问题

274 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情

有人相爱,有人夜里开车看海,我是leetcode第一题都做不出来

这是网易和百度面试过程中都出现过的一道题

旋转数组的最小数字

有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

要求:空间复杂度:O(1)O(1) ,时间复杂度:O(logn)O(logn)

示例1:

输入:[3,4,5,1,2]

返回: 1

示例2:

输入:[3,100,200,3]

返回: 3

解题思路

暴力解法可以挨个遍历数组,寻找最小值,但是子题目要求:空间复杂度:O(1)O(1) ,时间复杂度:O(logn)O(logn),这就需要用一些技巧了

首先旋转数组将原本有序的数组分成了两部分有序的数组,因为在原始有序数组中,最小的元素一定是在首位,旋转后无序的点就是最小的数字。旋转后我们可以把数组分为AB两个区域,可以知道A区域所有的值都大于B区域

用两个指针,分别指向最左侧left和最右侧right,再获取一个中间指针mid;以区间[left,right]的最右侧的值为基准,使中间值与它比较;慢慢缩小区域,当left大于等于right时,就找到了最小值

具体步骤如下:

  • 第一步:初始化左右指针,双指针指向旋转后数组的首尾,作为区间端点。
  • 第二步:当左指针小于右指针则进入循环
    • 计算获取中间指针
    • 若是区间中点值小于区间右界值,则最小的数字一定在中点左边。 right = mid
    • 若是区间中点值大于区间右界值,则最小的数字一定在中点右边。left = mid+1
    • 若是区间中点值等于区间右界值,则是不容易分辨最小数字在哪半个区间,应该逐个缩减右界令right--
  • 第三步:当left大于等于right时,就找到了最小值,返回rotateArray[left]
function minNumberInRotateArray(rotateArray){
  let left = 0
  let right = rotateArray.length-1;
  while(left < right){
    let mid = Math.floor( (left + right)/2 );
    if(rotateArray[mid] < rotateArray[right]){
      right = mid;
    }else if(rotateArray[mid] > rotateArray[right]){
      left = mid+1;
    }else{
      right--;
    }
  }
  return rotateArray[left];
}

image.png