11、旋转数组的最小数字

旋转数组的概念:

把一个数组最开始的若干个元素搬到数组的末尾,就是旋转数组。
例如: 数组{1,2,3,4,5}的一个旋转数组就是{3,4,5,1,2},把1,2放数组的后面。

题目:

  • 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素

思路:

这题最直观的解答就是遍历一遍数组,直接找出其中最小的元素。时间复杂度是O(n)。但是没有利用旋转数组的特性。
我们可以再来看看旋转数组:

旋转数组的特性:

  • 可以拆分为两个排好序的子数组
  • 前面的子数组的元素大于或者等于后面子数组的元素。
  • 最小的数组恰好是这两个数组的分界线。

举例说明:

旋转数组在一定程度上是排序的,因此试试用二分法找最小的元素。
直接举例说明,

我们以数组{3,4,5,1,2},指定两个指针,一个指向第一个元素3,一个指向最后一个元素2,如下图所示。位于两个指针中间的元素是5,它大于第一个指针指向的元素。因此中间数字5一定是前面的递增子数组中的元素,故最小的元素一定位于它的后面。所以移动第一个指针指向中间,如图所示,缩小查询范围。

这时,位于两个指针中间的元素是1,1小于第一个指针指向的元素,所以数字1位于后面的递增子数组中,故最小元素在数字1的前面或者就是数字1,这时将第二个指针指向数字1,缩小查询范围,如图所示。

此时,两个指针的距离是1,所欲第一个指针指向的是前面的递增子数组的最后一个元素,即最大的元素;第二个指针指向的是后面的递增子数组中第一个元素,即最小的元素,这就找到了。
在这里插入图片描述

代码(继续看下去)

def MinInOrder(array):
    if array == None or len(array) <= 0:   # 排除空集
        return false
    l = 0                    # 定义左指针
    r = len(array) - 1       # 定义右指针
    while(array[l] >= array[r]):  
        if r - l == 1:          # 当左右指针相邻时,说明找到了,右指针指向的就是最小的元素
            return array[r]
            break
        mid = l + ((r - l) >> 1)    # mid是中间元素的index
        if array[mid] > array[l]:   # 若中间元素大于左指针指向的元素,说明最小元素在其后,故缩小查询范围
            l = mid
        else:                   # 反之同理
            r = mid
    return array[mid]
输入:
array = [3,4,5,1,2]
MinInOrder(array)
输出:1

别急,还没有完,其实还有两个问题没考虑到

问题一:

前面说旋转数组是把前面部分元素搬到后面去,这没错,但是有个特殊情况,那就是我把前面0个元素搬到后面去,即旋转数组还是自己本身,哈哈哈,是不是有意思了。

这个时候,数组中最小的元素就是第一个元素了,我们试着运行上的代码:

输入:
array = [1,2,3,4,5]
MinInOrder(array)
输出:5

看见没有。输出的是5,最大的元素找出来,但我们要的是最小的元素,所以我们得改改代码

问题二:

看个例子,数组{0,1,1,1,1} 现在有两个旋转数组 {1,0,1,1,1} 和数组 {1,1,1,0,1}
在这里插入图片描述
看上图,会发现,左右指针指向的元素和中间元素是一样,这个时候,就不知道中间元素到底是前面的递增子数组还是后面的递增子数组,所以无法缩小查找范围。这时我们就得采用顺序查找的方式了。看完整的代码:

class Solution:
	def Min(self,array):
	    if array == None or len(array) <= 0:   # 排除空集
	        return False
	    l = 0                    # 定义左指针
	    r = len(array) - 1       # 定义右指针
	    mid = 0                  # 将指针mid初始为0,一旦发现数组第一个元素小于最后一个元素,即数组是排序的(问题一),这直接返回
	    while(array[l] >= array[r]):  
	        if r - l == 1:          # 当左右指针相邻时,说明找到了,右指针指向的就是最小的元素
	            return array[r]
	            break
	        mid = l + ((r - l) >> 1)    # mid是中间元素的index
	        
	        # 如果左指针、右指针指向的元素和中间元素一致时(问题二),采用顺序查找
	        if array[l] == array[mid] == array[r]:
	            return self.MinOrder(array,l,r)
	        
	        if array[mid] > array[l]:   # 若中间元素大于左指针指向的元素,说明最小元素在其后,故缩小查询范围
	            l = mid
	        else:                   # 反之同理
	            r = mid
	        
	        # 如果左指针、右指针指向的元素和中间元素一样,则只能顺序查找
	    return array[mid]
	
	def MinOrder(self,array,l,r):     # 顺序查找,循环遍历所有元素,找最小的元素
	    re = array[l]
	    for i in range(l+1,r):
	        if re > array[i]:
	            re = array[i]
	    return re

if __name__ == "__main__":

# 验证
    # 功能测试:输入数组升序数组的一个旋转数组,数组中有重复或在没有重复的数字
    test1 = [3,4,5,1,2]
    test2 = [1,0,1,1,1]
    
    # 边界测试:输入数组只有一个元素
    test3 = [5]
    
    # 特在测试:输入数组是空
    test4 = []
    
    solution = Solution()
    print("test_1:", solution.Min(test1))
    print("test_2:", solution.Min(test2))
    print("test_3:", solution.Min(test3))
    print("test_4:", solution.Min(test4))
输出:
test_1: 1
test_2: 0
test_3: 5
test_4: None

简写

def minNumberInRotateArray(rotateArray):
        # write code here
        # 二分查找:找左右的方法:右边的值大于中指时,说明最小值在左边,反之
        if rotateArray == None:   # 排除空集
            return 0
        left = 0          # 定义左指针
        right = len(rotateArray) -1   # 定义右指针
        while left <= right:
            mid = (right + left) >> 1     # 定义中间指针
            if rotateArray[mid] < rotateArray[mid-1]:    # 若中间指针对应的元素恰好小于前一个元素,说明当前元素就是最小的元素
                return rotateArray[mid]
            elif rotateArray[mid] < rotateArray[right]:  # 若中间元素小于最右边的元素,说明最小值在左边,更新指针
                right = mid-1
            else:                              # 反之
                left = mid+1
        return 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值