LeetCode搜索旋转排序数组 二分查找

33. 搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同 

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标  0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 

示例 1

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

输出:4

示例 2

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

输出:-1

示例 3

输入:nums = [1], target = 0

输出:-1

 

进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗?

思路:

看到题目搜索+升序排列+进阶要求logN,就是二分查找没跑啦。只不过这个题整了一点花样,不是完全升序,而是中间有个交换。所以二分查找策略的也要相应的调整,这也是这个题的难点。

如果不管进阶要求,遍历地去做,多半是要被挂的(被挂代码,请勿使用):

class Solution(object):

    def search(self, nums, target):

        for i in range(len(nums)):

            if nums[i]==target:

                return i

        return -1

二分查找的思路如下:

  比如现在有个数组:4 5 6 7 / 0 1 2 3,中间的/表示旋转边界,照常规二分查找思路,定义一个left在左边,right在右边,我们得到 mid = left+(right-left)//2,假如nums[mid]==target,那正好找到了,皆大欢喜,结束;否则,我们需要先判断,这个mid的位置是在旋转边界的“左边”还是“右边”。判断方式很简单,拿nums[mid]nums[left]比较,在这个例子中,第一轮的nums[left]4,如果nums[mid]大于4,则在旋转边界左边,反之在右边。

  我们只有先确定了mid在哪里,才能正确地判断targetmid的哪一边。

假如mid在旋转边界的左边,则target若处于nums[left]nums[mid]之间,target就在mid左边,否则在mid右边;

假如mid在旋转边界右边,则target若处于nums[target]nums[right]之间,target就在mid右边,否则在mid左边。

  这些只是看起来有点绕,但无非就是先确定mid的位置在旋转边界的左还是右,然后对于不同情况去判断targetmid的左还是右即可。因为二分查找的本质就是一遍遍地通过探索targetmid的相对方向来逼近结果的,对吧?

代码:

class Solution(object):

    def search(self, nums, target):

        lenth = len(nums)

        left = 0

        right = lenth-1

        while(right>=left): #二分查找 我习惯写上等于号 

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

            if target==nums[mid]:

                return mid

            if  nums[mid]<nums[left]:#mid在右段

#左边界和右边界就写left和right 不是0和-1

                if target>nums[mid] and target<=nums[right]:#如果target在mid的右边

                #最关键就是上面这个条件是个范围

                    left=mid+1

                else:#太大了以至于来到了左边或者更小时,就在左边

                    right=mid-1

            else:#mid在左段

                if target>=nums[left] and target<nums[mid]:#若target在mid左边

                    right=mid-1

                else:#target在mid右边

                    left=mid+1

        return -1

小结:

  代码里写的mid = left+(right-left)//2,和(left+right)//2其实没太大区别,只不过前者可以避免left和right都很大时两数相加太大超内存。不过应该也不至于,反正差不多,可以作为一个写二分查找时无关紧要的习惯,不用太纠结。

  对于二分查找搜索的变式一共也就没几道题,一半理解一半记忆就可以搞定了,这道搜索旋转排序数组还是比较经典的。

posted @   JunanP  阅读(2)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示