python旋转排序数组_[leetcode Python]-二进制搜索-153在旋转排序数组中查找最小值,leetcodePython,二分,153FindMinimuminRotatedSorte...

目录

题目链接

题目描述

一个递增数组以数组某个未知位置为枢纽进行旋转(具体表现为,将数组最开始的若干个元素(可以为0个)搬到数组末尾),输出旋转数组的最小元素。

示例

输入:nums = [3,4,5,1,2]

输出:1

输入:nums = [4,5,6,7,0,1,2]

输出:0

输入:nums = [11,13,15,17]

输出:11

解题思路一

如果遍历数组寻找最小值的话时间复杂度为O(N)。

利用

二分搜索

算法寻找最小值,时间复杂度能降到O(logN)。二分搜索的应用范围不仅限于排序后的序列问题,只要问题能够通过某个binary dicision一步步缩小搜索范围即可应用。在本题中,nums中的数字在旋转后可以分为两部分,一部分都比nums最后一个元素大(可称为左排序数组),另一部分都小于或等于nums最后一个元素值(可称为右排序数组)。举个例子,旋转后的数组 [4,5,6,7,0,1,2]中,[4,5,6,7]这个升序序列属于一部分,且都比2大;[0,1,2]这个升序序列属于另一部分,均小于等于2。因此,可以

将寻找旋转数组最小值的问题转化为寻找数组中第一个比nums[-1]小的值的问题。

如果nums[0] < nums[-1],说明数组没有经过旋转,最小值是nums[0],这种条件下直接return nums[0]即可。

在实现时,设置target_index来保存数组中第一个比nums[-1]小的值的索引,在进行二分搜索时,应该如何缩小搜索范围呢?初始化 l = 0,r = len(nums) - 1,mid = (l + r)/2。当nums[mid]比nums[-1]大时,说明nums[mid]属于左边部分,那么就要把nums[mid]和nums[mid]左边的所有值都从当前搜索范围中去除(l = mid + 1)。如果nums[mid]

58a09d9380101e9ddb35739e3dd12079.gif

nums[-1],说明nums[mid]在右边部分,nums[mid]有可能是第一个比nums[-1]小的值,也可能是第n个比nums[-1]小的值,用一个target_index变量来暂存mid的值,并将nums[mid]及右边的所有值都从当前搜索范围内去除(r = mid - 1)。当r

ee5ed66540e2453fb2e00a446b45818a.png

需要注意的是,只要当前循环 l

58a09d9380101e9ddb35739e3dd12079.gif

r,就有mid

58a09d9380101e9ddb35739e3dd12079.gif

tmp_index,因为搜索范围一直在缩小,当前循环的二分位置索引(mid)一定比上一层循环的二分位置索引(用tmp_index来存储)要小。

第一种实现方式中目标值的搜索范围为左闭右闭区间[l,r],因此循环停止条件为 l<=r,并且在特定条件下更新时为left = mid + 1,right = mid -1。但是这种实现不适用于数组中有重复元素的情况。

解题思路一Python实现

class Solution:

def findMin(self, nums: List[int]) -> int:

if(nums[-1]>nums[0]):

return nums[0]

#问题转变为找出第一个比nums[-1]小的数

tmp_index = len(nums)-1

l = 0

r = len(nums)-1

while(l <= r):

mid = (l + r)//2

if(nums[mid]> nums[-1]): #如果当前项属于第一部分

l = mid + 1 #当前项以及当前项左边的所有项都不再考虑

else: #mid一定是小于或等于当前的tmp_index

tmp_index = mid

r = mid -1

return nums[tmp_index]

二分法模板

参考

知乎回答

中用户Jason Li给的二分法模板和讲解,

解决寻找非降序数组中第一个不小于value的值的问题

。在该模板中,left初始化为0,right初始化为len(nums)。

将目标值的搜索范围限定在左闭右开区间[left,right)中

,跳出循环的条件为 left< right,因为此时[left,right)区间为空。在特定条件下更新right索引的方法为right = mid。循环跳出后返回nums[left]或者nums[right]都可以,因为此时left和right是重合的。在搜索过程中nums的索引被分为三部分:[0,left)左闭右开区间、[left,right)左闭右开区间、[right,len(nums))左闭右开区间。其中[first,left)范围内的元素都小于value,[right,last)范围内的元素都大于等于value。

当nums[mid]小于value时,nums[mid]理应在[first,left)这一段元素都小于value的区间范围内,因此需要令left = mid + 1扩充[first,left)区间;

当nums[mid]大于等于value时,nums[mid]理应在[right,last)这一段元素都大于等于value的区间范围内,因此需要令right = mid 来扩充[right,last)区间。

等到循环结束,left = right时,nums中第一个不小于value的值应该是[right,last)区间范围的第一个值,也就是nums[right],由于此时right = left,因此也可以返回nums[left]。(这样写省了很多麻烦,比如纠结返回left还是right的问题)

下面的模板适合直接处理在nums中求下界的问题:

def binarySearch(nums,left,right,value): #返回nums中第一个不小于value的数值

while(left < right):#[left,right)不为空

mid = (left + right) // 2 #Python使用长整型,不会有溢出风险。其他语言可以用mid = left + (right - left) //2 来得到中间位置的索引

if(nums[mid] < value): #在这种条件下,要寻找的nums中第一个不小于value的数值在mid的右边

left = mid + 1

else:

right = mid

return nums[left]

如果改为求上界(求nums中满足x < value的最大x),可以用求下界的函数求出下界再减去1,因为

a5541b8aff03099f1540a58d4c9bf2a3.gif

的下界减去1就是

2c6c86e9f7a6254be872ba0dc8959c22.gif

的上界。同样的,x > value的下界减去1就是

e442963d50dbf4a5fcb2bc3468091b3f.gif

的上界。如下图所示:

9ce0ae467bf6608f9fa1cce8e56158e0.png

此求下界的模板方法

同样适用于nums中有重复元素的情况和找不到满足条件的元素的情况

,可以记住这种写法。

nums中有重复元素

: 在此模板方法下,[0,left)左闭右开区间和[right,len(nums))左闭右开区间被不断扩充,扩充到left = right时,right和left所指的位置是第一个大于等于value的元素。即便有重复元素,但是mid应该属于[0,left)区间还是[right,len(nums))区间只根据nums[mid]和value的大小决定。不会存在无法划分的情况。而在

154 Find Minimum in Rotated Sorted Array II

问题中,旋转数组中可能有重复元素,当nums[mid] = nums[right]时,无法确定mid属于左排序序列还是右排序序列,因此需要进一步处理,详见【leetcode-Python】- Array类型 - 154  Find Minimum in Rotated Sorted Array II

nums中找不到满足条件的元素

:在这种情况下nums中所有元素都小于value,left在循环中不断向右移动(left = mid + 1)直到和right重合,跳出循环后,left = right = len(nums),再根据题目的不同要求返回具体数值。如题目要求找不到满足条件的元素则返回-1, 跳出循环后的return语句就可以写成 return left

给出寻找旋转数组中最小值问题的另一种实现方式(推荐),不断扩展左排序数组(大于nums[right])和右排序数组(小于等于nums[right])的范围。到left = right时循环跳出,此时两个指针都指向右排序序列的第一个元素

class Solution:

def findMin(self, nums: List[int]) -> int:

left = 0

right = len(nums)-1

while(left

mid = left + (right - left) // 2

if(nums[mid] > nums[right]): #说明mid在左排序数组

left = mid + 1

else:

right = mid #如果nums[mid] <= nums[right],说明mid和right都在右排序数组

return nums[left]

解题思路二

其实Python可以直接利用min()函数来返回list中的最小元素。

class Solution:

def findMin(self, nums: List[int]) -> int:

return min(nums)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值