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

50 阅读1分钟

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

Easy

方法一(暴力)

思路

不管怎么旋转交换数组,最小的永远都是那个最小的

时间复杂度O(n)O(n)

所以,直接返回最小值,AC!

代码

python3

class Solution:
    def minArray(self, numbers: List[int]) -> int:
      return min(numbers)

方法二(二分搜索)

思路

题目中数组形成的方式是,在一个有序的数组中,截取的前面一段,放到了后面。那我们只要找到转换的这个分割点,就是我们想要的答案。

  • 分析一下,题目中数组的特征:

    • 数组是由两个递增数组拼接起来得到的,按照这样的方式数组可以分成两个区域
    • 两个区域自身都是单调增
    • 后面一个区域的所有元素都小于等于前一个区域的所有元素
  • 二分搜索对于中间值和区间[i,j],有三种情况:

    • numbers[mid] > numbers[j] 中点值比右边界的值大,说明mid节点在区域一(大的那个区域),转换点肯定在右侧
    • numbers[mid] < numbers[j] 中点值比右边界小,应为区域一里面的每个数都比区域二中的小,所以转换点肯定是在左边。特殊情况:假如区间[i,j]本身是个单调增数组,说明转换点是0,同样满足在左侧区域
    • numbers[mid] == numbers[j] 中间值和右边界相同,无法判断,应为元素可以是相等的,如:1,1,2,2,21,2,2,1,2一个转折点是在左边区域,一个在右边区域。
  • 对于第三种情况,我们使区间变成[i,j-1],比较的相同,说明将边界j去掉之后,对于结果是没有影响,即使j的值是最小的也有mid和他相同。(注意,应为这道题是返回旋转的节点的值,我们可以这样简单处理)

  • 为什么都是比较右边界? 比较左边界i,无法确定是在哪个区域。比如,1,2,3,4 这样的列表,转换点是index:0的位置,当numbers[mid] > numbers[i] 时在右边,列表为4,1,2,3在左边,不好确认。比较j边界不会出现这种情况,应为j都是在右边区域。

以上,尝试写一下代码,AC!

代码

python3

class Solution:
  def minArray(self, numbers: List[int]) -> int:
    i = 0
    j = len(numbers) - 1
    while i < j:
      mid = i + (j-i) // 2 
      if numbers[mid] < numbers[j]:
        j = mid
      elif numbers[mid] > numbers[j]:
        i = mid + 1
      else:
        j -= 1
    return numbers[i]