[路飞]_剑指 Offer 11. 旋转数组的最小数字

2,481 阅读2分钟

「这是我参与2022首次更文挑战的第32天,活动详情查看:2022首次更文挑战

剑指 Offer 11. 旋转数组的最小数字

题目

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为1。  

示例1

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

示例2

输入: [2,2,2,0,1]
输出: 0

题解

初看题目很简单,一行代码解决

var minArray = function(numbers) { return Math.min(...numbers) };

细看一下原来是要求用二分法来求解。一行代码虽然简单,但是时间复杂度O(N);现在题目条件给出数组是排序的只是经过了一次反转。要利用数组在一定范围有序这一个特性。

话说二分法是真的要多思考理解啊,这题目都把二分玩出花来了。

二分法

数组是经过反转的数组;有什么条件分割数组呢?

假设现在数组左侧指针为:leftleft,右侧指针为:rightright;并且最小值在[left,right][left,right]区间

对于数组中 numbers[right]numbers[right],一定存在:

  • 最小值右侧的元素,它们的值一定都小于等于 numbers[right]numbers[right]
  • 最小值左侧的元素,它们的值一定都大于等于numbers[right]numbers[right]

现在找[left,right][left,right]区间的中间值 midmid

  • 如果 numbers[mid]>numbers[right] numbers[mid] > numbers[right]midmid 落在最小值的左侧;最小值在[mid+1,right][mid+1,right]之间
  • 如果 numbers[mid]==numbers[right]numbers[mid] == numbers[right] 遇到重复元素了,最小值在[left,right1] [left,right-1] 区间
  • 上述两种可能都不是最小元素只能在[left,mid][left,mid]区间了
var minArray = function (numbers) {
  let len = numbers.length
  let left = 0
  let right = len - 1
  while (left < right) {
    let mid = Math.floor(left + (right - left) / 2)
    if (numbers[mid] > numbers[right]) {
      left = mid + 1
    } else if (numbers[mid] == numbers[right]) {
      right = right - 1
    } else {
      right = mid
    }
  }
  return numbers[left]
}

结语

作者水平有限,如有任何意见和建议欢迎评论区留言讨论