LeetCode之团灭四道搜索旋转数组

搜索旋转数组是面试中很容易考到的题目,我面试次数不多,都已经碰到两次,一次在头条,一次在作业帮。在LeetCode中出现了四道关于搜索旋转数组的题目。

搜索旋转数组 就是 对数组进行二分法的灵活运用。
33和81寻找是否存在某值,33数组里面元素不重复,81里面可重复。
153和154寻找最小值,153数组元素不重复,154数组元素可重复。

一、原型:

首先上二分查找的原型题目:LeetCode 704 题 :二分查找

def search(nums, target):
    l = 0
    r = len(nums) - 1
    while l <= r:
       mid = (l + r) // 2
       if nums[mid] == target:
           return mid
       if nums[mid] < target:
           l = mid + 1
       else:
           r = mid - 1
    
    return -1
           

二、搜索旋转排序数组

看完原型然后就看33题:
题目:假设按照升序排序的数组在预先未知的某个点上进行了旋转。 ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 你可以假设数组中不存在重复的元素。 你的算法时间复杂度必须是 O(log n) 级别。

了解了什么是旋转数组,我们可以通过中点和两端的相比较得知,那边是有序的,那边是有旋转点的。正常来说,一个升序数组最左边nums[left] 是要小于nums[mid],  如果出现了nums[left] > nums[mid] ,就可以说明左一半出现旋转点,右一半是有序的。我们只需要做到两步:找到有序的一半,判断target是否在找到有序的一半。

def search(nums, target):
    l = 0 
    r = len(nums) - 1
    while l <= r:
       mid = (l + r)//2
       if nums[mid] == target:
          return mid          # 目前为止和 原型一样的代码
       if nums[l] <= nums[mid]: # Step 1、找到有序的一半:左边有序,易错1:因为mid 可能 = l,所以不能去掉 = 
          if nums[l] <= target <= nums[mid]: # Step 2、判断target是否在有序的一半
             r = mid - 1
          else:
             l = mid + 1
       else:                     # 右边有序
          if nums[mid] <= target <= nums[r]:
             l = mid + 1
          else:
             r = mid - 1
     return -1

81. 搜索旋转排序数组 II :如果存在多个重复元素,就会出现一些case:  [1, 3, 1, 1, 1]
我们在33题中通过对nums[left] 和 nums[mid] 做大小比较来找到有序的一半, 但是如果nums[left] = nums[mid]= nums[right]就没法利用这个对比了。

所以我们加多了了一个if分支来处理这种情况:左指针不断往右移,相当于遍历数组了。

if nums[mid] == nums[left]:
   left += 1

完整代码如下:

def search(nums, target):
    left = 0
    right = len(nums) - 1
    
    while left <= right:
        mid = (left + right) // 2
        if nums[mid] == target: 
            return True
        if nums[mid] == nums[left]:  # 和33的不同点
            left += 1
        elif nums[mid] > nums[left]:
            if nums[mid] > target >= nums[left]:
                right = mid - 1
            else:
                left = mid + 1
        else: 
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1
                
    return False

三、寻找旋转排序数组中的最小值

153和154题:正常来说,升序数组中最小值就是nums[left], 现在是旋转数组,所以是要找到旋转数组的旋转的那一部分的第一个元素

这两道题和二的两道题有下面的两点不同:

  • 上面两道题在二分时候都是mid-1 ,mid+1,把mid排除在外,在下面这两道题中会把mid也考虑在里面
  • 如果是nums[right] 和 nums[mid] 做比较,那么不用处理;
    如果是nums[left] 和 nums[mid] 作比较,那么要加多一个初试判断:
    if nums[l] < nums[r]:  # 如果已经二分到全部是有序的了,就返回第一个
                return nums[l]   
    至于为什么会这样,可以试试用一个有序数组[1,3,5] 手动试试
  • 33和81:查看是否存在某元素,即有可能不存在,所以要用while l <= r
     153和154: 找最小值,一定存在。所以用while l < r,不用加=

下面给出两道题代码:

153:

(1)nums[left] 和 nums[mid] 作比较

def findMin(nums):
    l = 0
    r = len(nums) - 1
    while l < r:
        mid = (l + r) // 2
        if nums[l] < nums[r]:
            return nums[l]
        if nums[l] > nums[mid]: 
            r = mid 
        else:                
            l = mid+1
    return nums[l]

(2)nums[right] 和 nums[mid]作比较

def findMin(nums):
    l = 0
    r = len(nums) - 1
    while l < r:
        mid = (l + r) // 2
        if nums[r] < nums[mid]: # 旋转数组第一个元素在右边,而且不包括mid
             l = mid + 1
        else:                   # nums[mid] <= nums[r]
             r = mid            # 旋转数组第一个元素在左边,包括mid
    return nums[l]

154: 多了处理重复元素的分支

def findMin(nums):
        l, r = 0, len(nums) - 1
        while l < r:
            mid = (l+ r) // 2
            if nums[mid] > nums[r]: l = mid + 1
            elif nums[mid] < nums[r]: r = mid
            else: r = r - 1 # 多了处理重复元素的分支
        return nums[l]

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值