zoukankan      html  css  js  c++  java
  • 《剑指 Offer》学习记录:题 11:旋转数组的最小数字

    题 11:旋转数组的最小数字

    题干

    把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。——《剑指 Offer》P82

    测试样例

    样例一

    普通测试点。
    输入:

    [3,4,5,1,2]
    

    输出:

    1
    

    样例二

    带有重复数字的数组的旋转。
    输入:

    [2,2,2,0,1]
    

    输出:

    0
    

    样例三

    最小值是数组的最后一个元素。

    [2,2,2,0]
    

    输出:

    0
    

    样例四

    最小值是数组的第一个或最后一个元素。

    [1,2,1]
    

    输出:

    1
    

    样例五

    数组旋转的元素个数是 0 个,也就是传了原始的数组进来。

    [1,2,3]
    

    输出:

    1
    

    样例六

    数组中只出现过一个数字。

    [1,1]
    

    输出:

    1
    

    遍历查找

    解题思路

    这个做法没什么好说的,就是直接遍历一遍找到数组的最小值。虽然不能减少 O(n),但是可以减少 T(n)。观察上述的样例,显然当 nums[i] < nums[i-1] 时 nums[i] 就是数组的最小值。如果用这种方式找不到最小值,说明传入的数组的旋转元素个数为 0(对应样例五、六),这时应该返回第一个元素。

    题解代码

    class Solution:
        def minArray(self, numbers: List[int]) -> int:
            for i in range(1, len(numbers)):
                if numbers[i] < numbers[i - 1]:
                    return numbers[i]
            return numbers[0]
    

    时空复杂度

    最坏情况下需要把数组遍历一遍,所以时间复杂度为 O(n)。
    由于只需要常数个数的辅助变量,因此空间复杂度为 O(1)。

    二分查找

    解题思路

    这种方法是比较聪明的,观察下图所示的样例,发现所谓数组的旋转是把原本的数组分割成了 2 个递增的序列,且第一个序列的元素均不小于第二个序列的任何元素,而最小值是 2 个序列的界限。

    此时的目的就变成了找到第 2 个递增序列的第一个元素,可以使用二分法来查找。定义 2 分查找的 3 个变量 high、mid、low,根据第一个序列的元素均不小于第二个序列的任何元素的特点,当 nums[mid] > nums[low] 时,说明待查找的元素在 mid 的右侧,需要执行 “high = mid + 1”;当 nums[mid] < nums[low] 时说明待查找的元素在 mid 的左侧,需要执行 “low = mid”。最终当 high >= low 时查找结束,直接返回 nums[high] 就行。
    例如上面的样例,初始情况下 3 个变量 high、mid、low 的状态如下。此时由于 nums[mid] > nums[low] 说明待查找的元素在 mid 的右侧,需要执行 “high = mid + 1”。

    第一轮二分以后 3 个变量 high、mid、low 的状态如下,此时由于 nums[mid] < nums[low] 说明待查找的元素在 mid 的左侧,需要执行 “low = mid”。

    第二轮二分以后 3 个变量 high、mid、low 的状态如下,此时由于 nums[mid] < nums[low] 说明待查找的元素在 mid 的左侧,需要执行 “low = mid”。

    第三轮二分以后 3 个变量 high、mid、low 的状态如下,此时由于不满足 high < low 的条件,二分查找结束返回 nums[high] 解决问题。

    注意使用二分时可以能会遇到 nums[mid] = nums[low] 的情况,此时无法判断说明待查找的元素在数组的什么地方。遇到这种情况有 2 种解决方法,一种是直接使用遍历查找,另一种是用减治法令 low = low - 1 减小解空间的范围,其实换成遍历查找也是减治法。

    题解代码

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

    时空复杂度

    由于使用二分法一次可以将查找范围缩小一半,所以时间复杂度为 O(㏒n)。
    由于只需要常数个数的辅助变量,因此空间复杂度为 O(1)。

    参考资料

    《剑指 Offer(第2版)》,何海涛 著,电子工业出版社
    面试题11. 旋转数组的最小数字(二分法,清晰图解)

  • 相关阅读:
    POJ1006: 中国剩余定理的完美演绎(非原创)
    poj 1001 分析
    document.createElement()的用法
    js innertext
    转csdn-css4
    css中最基本几个选择器
    Django解决(1146, "Table 'd42.django_session' doesn't exist")方法
    django清理migration终极解决办法
    linux中的fork炸弹
    nginx转发php文件到php-fpm服务器提示502错误
  • 原文地址:https://www.cnblogs.com/linfangnan/p/15063983.html
Copyright © 2011-2022 走看看