前言
这个点写博客,真的是有点被逼无奈呀,室友每天晚上都睡得很晚(不是打游戏,是在学习),我是基本全黑的情况下还要酝酿很久才能睡着的人,所以更别提有光和有鼠标键盘声的环境下了,既然睡不着就起来刷题写博客吧,总比在床上玩手机来的好!所以这应该算是我的第一篇深夜博客吧,以后估计还会有更多吧。。。
问题描述
解法一
看到这个问题第一个想法肯定就是二分法呀,又要求了时间复杂度在log(n),所以二分法没跑了。但是这个二分法写起来却有点麻烦,因为边界条件可能不是那么好写。下面的代码就是我的垃圾解法,虽然很垃圾,但还算逻辑清晰。下面简单介绍一下思路:
首先先二分法取一个点current,然后和target比较,如果比目标值大,则说明应该向递减的方向上移动,反之则向递增的方向上移动。如果是完全排好序的列表,那就很好解决啦,直接左右移动即可,但是现在因为出现了旋转,所以就得判断此时递增的方向在哪,递减的方向在哪了。
判断的大致逻辑是这样的:
首先先将位于列表左边的递增序列称为左上升区,右边的递增序列称为右上升区,同时可以发现,左上升区的所有值都大于右上升区的值。
当我们已经知道了current和target的大小后,如果需要找到更大的值,先判断current是在左上升区还是在右上升区,判断的方法就是用current和list中的最后一个值(也就是右上升区的最大值进行比较),如果current比其大,则说明current肯定在左上升区,那么此时比current大的值肯定在current的右边,所以begin=current,end不变,下一个区间就向右方移动。而如果current在右上升区,那么此时比current大的值既可能在current的右边,也可能在current的左边,因此这个时候还得判断,target是位于左区还是右区,判断方法同样是和list的最后一个元素比较,如果大则说明target位于左上升区,则向current的左边搜索,反之向右边搜索。这样算是讨论好了一种情况,同样地,对于current在右上升区的情况做类似的讨论。
估计你看到这块就已经很迷糊了,说实话,我也迷了,唉,看来表述能力还有待提高呀,也可能是深夜发贴的原因吧。。。有啥不明白的直接评论即可!
class Solution:
def search(self, nums: List[int], target: int) -> int:
n = len(nums)
if n==0:
return -1
begin = 0
end = n-1
current = (int)((begin+end)/2)
while end >= begin:
print(current)
if nums[current] == target:
return current
elif nums[current]>target:#往小的方向变
if nums[current] > nums[end]:#当前位置在左边上升区
if target >nums[end]:#目标位于左边
end = current-1
else:#目标位于右边
begin = current+1
else:#当前位置在右边上升区 目标只能位于左边
end = current-1
else:#往大的方向变
if nums[current] > nums[end]:#当前位置在左边上升区
begin = current+1#目标位置只能位于当前位置右边
else:#当前位置在右边上升区
if target >nums[end]:#目标位于左边
end = current-1
else:#目标位于右边
begin = current+1
current = (int)((begin+end)/2)
return -1
解法二
其实上面的解法虽然也是log(n)的复杂度,但是分析起来很麻烦,逻辑错一点就会有问题,其实有一种更好的方法(看了第一名的代码后才想到的),就是先确定list旋转点的位置,然后再确定target是落在左上升区还是右上升区,如果旋转点位置已知,target位于的分区也已知,那么就可以只在这个分区内找target就行啦,而这个分区是排好序的,最适合二分查找。然后就搞定了,逻辑清晰,步骤明确,我怎么就想不到呢?果然,人和人之间的差距还是蛮大的。。。。
由于这个想法刚看到,还没时间来的及实现,现在也凌晨一点了,室友也要睡觉了,那就留给明天实现吧。。。
class Solution:
def search(self, nums: List[int], target: int) -> int:
n = len(nums)
if n==0:
return -1
begin = 0
end = n-1
pivot = (int)((begin+end)/2)
if nums[0]>nums[n-1]:#如果有旋转 则这个等式一定成立 否则就完全是一个递增的列表 可直接进行二分查找
while end > begin:#寻找旋转点
if nums[pivot]>nums[pivot+1]:
break
elif nums[pivot]>nums[end]:
begin = pivot
else:
end = pivot
pivot = (int)((begin+end)/2)
if target > nums[n-1]:#目标在左上升区 设置二分查找区间
begin = 0
end = pivot
else:#目标在右上升区 设置二分查找区间
begin = pivot+1
end = n-1
current = (int)((begin+end)/2)
while end >= begin:#进行二分查找
if nums[current]==target:
return current
elif nums[current]<target:
begin = current+1
else:
end = current-1
current = (int)((begin+end)/2)
return -1
并不是“明天”实现的,而是“后天”才实现的。。。。。
总结
第一次深夜写博客,写的很烂,思路感觉都不清晰,不过总比睁着眼睡不着觉好吧。。。
晚安,各位!