• 【Leetcode刷题】旋转数组的最小数字


    https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/

    朴素遍历法

    class Solution(object):
        def minArray(self, numbers):
            """
            :type numbers: List[int]
            :rtype: int
            """
            # 设最小值所处位置为i,数组中最小值的特性是:numbers[i-1] > numbers[i]
            # 而数组中处于其他位置j的数字都满足 numbers[j-1] <= numbers[j]
            n = len(numbers)
            for i in range(n):
                if numbers[i - 1] > numbers[i]:
                    return numbers[i]
            # 所有元素都相等
            return numbers[0]
    

    时间复杂度:O(n)

    空间复杂度:O(1)

    二分查找法

    使用二分查找缩小搜索范围,降低时间复杂度

    下面的解法是错误的

    class Solution(object):
        def minArray(self, numbers):
            """
            :type numbers: List[int]
            :rtype: int
            """
            # 设最小值所处位置为i,数组中最小值的特性是:numbers[i-1] > numbers[i]
            # 而数组中处于其他位置j的数字都满足 numbers[j-1] < numbers[j]
            # 使用二分查找找到这个位置
            # 如何缩小搜索范围:
            # 若numbers[0] > numbers[j],说明最小值的位置在j左侧
            # 若numbers[0] < numbers[j],说明最小值的位置在j右侧
            # 若numbers[0] = numbers[j],说明numbers[0,j]的值都相同,则最小值的位置在j右侧
            left = 0
            right = len(numbers) - 1
            while left <= right:
                print(left, right)
                mid = (left + right) // 2
                # 当mid=0时,numbers[mid-1]表示numbers最后一个数字
                if numbers[mid] < numbers[mid - 1]:
                    return numbers[mid]
                if numbers[left] <= numbers[mid]:
                    left = mid + 1
                else:
                    right = mid - 1
            # 所有元素都相等
            return numbers[right]
    

    错误的原因是:不能与数组中第一个元素比较来缩小查找范围

    因为当numbers[left] < numbers[mid]时,由于numbers[left]可能是最小值,因此不能直接舍弃掉mid左半边。这个问题导致了无法通过一次比较舍弃掉数组的一半

    而与数组中最后一个元素比较则能很好地将数组分成两部分。

    numbers[mid] < numbers[right]时,说明最小值在mid的左侧,且最小值有可能是numbers[mid],但不可能是numbers[-1],因此可以将numbers[mid+1:]这部分舍弃

    numbers[mid] > numbers[right]时,说明最小值在mid的右侧,且最小值有可能是numbers[-1],但不可能是numbers[mid],因此可以将numbers[:mid+1]这部分舍弃

    相等的情况在上面的解法中也没有考虑得很全面,下面我们着重分析

    numbers[mid] == numbers[right]时,最小值可能在mid的右侧,即numbers[mid:]先递增后递减;也可能在mid的左侧,即numbers[mid:]的值全部相等。那么这怎么办?

    根据官方题解:

    我们唯一可以知道的是,由于它们的值相同,所以无论 numbers[-1] 是不是最小值,都有一个它的「替代品」numbers[mid] ,因此我们可以忽略二分查找区间的右端点。

    由以上思路可得到正确的二分查找写法:

    class Solution(object):
        def minArray(self, numbers):
            """
            :type numbers: List[int]
            :rtype: int
            """
            left = 0
            right = len(numbers) - 1
            while left <= right:
                mid = (left + right) // 2
                # 当mid=0时,numbers[mid-1]表示numbers最后一个数字
                if numbers[mid - 1] > numbers[mid]:
                    return numbers[mid]
                if numbers[mid] < numbers[right]:
                    # 前一个判断已经排除了最小值是numbers[mid]的可能
                    right = mid - 1
                elif numbers[mid] > numbers[right]:
                    left = mid + 1
                else:
                    # 相等时,忽略右端点
                    right -= 1
            # 所有元素都相等
            return numbers[0]
    

    时间复杂度:O(logn)

    空间复杂度:O(1)

    优化

    考虑想办法省去numbers[mid - 1] > numbers[mid]这个判断

    最终,搜索区间收敛到left = right - 1,则mid = left,left左边是一个递增序列,right右边也是一个递增序列

    numbers[mid] < numbers[right]时,即numbers[left] < numbers[right],又因为left左侧序列递增,则必然有numbers[left-1] > numbers[left],因此此时结束循环返回numbers[left]即可

    numbers[mid] > numbers[right]时,很明显,numbers[right]就是要找的最小值

    numbers[mid] == numbers[right]时,返回任一即可

    最终优化的代码为

    class Solution(object):
        def minArray(self, numbers):
            """
            :type numbers: List[int]
            :rtype: int
            """
            left = 0
            right = len(numbers) - 1
            while left < right:
                mid = (left + right) // 2
                if numbers[mid] < numbers[right]:
                    # 此时mid有可能是最小值,应加入搜索区间
                    right = mid
                elif numbers[mid] > numbers[right]:
                    left = mid + 1
                else:
                    # 相等时,忽略右端点
                    right -= 1
            # 所有元素都相等
            return numbers[left]
    
  • 相关阅读:
    康师傅JVM:运行时数据区概述及线程(三)
    康师傅JVM:程序计数器(四)
    Git常用命令
    Arthas概述
    康师傅JVM:JVM与Java体系结构(一)
    LabVIEW 连接MySQL数据库
    LabVIEW dll 崩溃
    LabVIEW 关于定时的研究
    NI 配置管理软件MAX的一些功能使用介绍
    LabVIEW 串口通信
  • 原文地址:https://www.cnblogs.com/luozx207/p/13360420.html
Copyright © 2020-2023  润新知