6.2.2 python二分查找算法及LeetCode题目(2)之旋转的数组 —— Search in Rotated Sorted Array

这一节总结几道关于Rotated 数组的题目,涉及到LeetCode内四道相关题目:

33. Search in Rotated Sorted Array

81. Search in Rotated Sorted Array II

153. Find Minimum in Rotated Sorted Array

154. Find Minimum in Rotated Sorted Array II

 

数组在排序的基础上略做变换,但是仍然维持有序,下面从几个角度解析这一类问题。

153. Find Minimum in Rotated Sorted Array

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e.,  [0,1,2,4,5,6,7] might become  [4,5,6,7,0,1,2]).

Find the minimum element.

You may assume no duplicate exists in the array.

Example 1:

Input: [3,4,5,1,2] 
Output: 1

题目解析:

我们先从这一题入手,在旋转数组中先找最小的那个数。

旋转数组的特点是,通过一个pivot将该数组分成了两部分,与第一个元素相比,左半部都比它大,右半部都比它小。

找最小的那个数,其实是找位置的一类题,这个pivot 具有这样的特点:左边的元素大,右边的元素也大,但是这样无法二分查找;因此还要在过程中与数组第一个元素相比来判断当前查找的位置在左右哪一部分,不断逼近pivot位置。需要注意的是,要处理一下数组没有被旋转的情况。代码如下:

class Solution:
    def findMin(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        l = len(nums)
        if l == 0:
            return None
        if l == 1:
            return nums[0]
                
        p = nums[0]        
        if nums[-1] >= p:
            return p
        
        left, right = 0, l-1        
        while True:
            mid = (left + right) // 2
            if mid + 1 < l and nums[mid] > nums[mid+1]:
                return nums[mid+1]
            
            elif nums[mid+1] > p:
                left = mid + 1
            
            elif nums[mid] < p:
                right = mid - 1

33. Search in Rotated Sorted Array

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).

You are given a target value to search. If found in the array return its index, otherwise return -1.

You may assume no duplicate exists in the array.

Your algorithm's runtime complexity must be in the order of O(log n).

Example 1:

Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4

题目解析:

此题有两大思路:

一,按照上一题的思路,先找到最小的数,即旋转的pivot及其位置,然后确定target所在部分(左部/右部),再二分查找。

二,直接二分查找套路,但是要判断 `target < nums[0]` 和`nums[mid]<nums[0]` ,二者同时成立时,按照普通的二分查找操
作;不同时成立,那么说明target和mid在不同部分,分情况处理。

两种方法代码如下,第一种方法看似复杂,实际上性能也是不错的,当我们确定了left和right后,直接套用格式,十分简洁;

第二种方法,在二分查找的格式上,再加一层判断,不理解原理会看的一头雾水。

class Solution:
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        l = len(nums)
        if l == 0:
            return -1
        elif l == 1:
            return 0 if target == nums[0] else -1
    
        left, right = 0, l-1
        pos = 0
        
        while left <= right:
            mid = (left + right) // 2
            if mid+1 < l and nums[mid] > nums[mid+1]:
                pos = mid+1
                break
            elif nums[0] <= nums[mid]:
                left = mid+1
            else:
                right = mid-1
            
        # print(pos)
        if target == nums[0]:
            return 0
        elif target == nums[pos]:
            return pos
        elif pos > 0 and target >= nums[0] and target <= nums[pos-1]:  
            left, right = 0, pos-1
        elif pos == 0 or target >= nums[pos] and target <= nums[l-1] :
            left, right = pos, l-1
        else:
            return -1
        
        while left <= right:
            mid = (left + right) // 2
            if target == nums[mid]:
                return mid
            elif target < nums[mid]:
                right = mid-1
            else:
                left = mid+1
        return -1
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        
        l = len(nums)
        if l == 0:
            return -1
        elif l == 1:
            return 0 if nums[0] == target else -1
        
        lt, rt = 0, l - 1
        p = nums[0]
        while lt <= rt:
            mid = lt + (rt - lt) // 2
            
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                if (nums[mid] < p) == (target < p):  # mid 和 target 在同一方                    
                    lt = mid + 1
                else:
                    rt = mid - 1
                    
            else:
                if (nums[mid] < p) == (target < p):
                    rt = mid - 1
                else:
                    lt = mid + 1
        return -1

154. Find Minimum in Rotated Sorted Array II

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e.,  [0,1,2,4,5,6,7] might become  [4,5,6,7,0,1,2]).

Find the minimum element.

The array may contain duplicates.

题目解析:

该题与81题背景一样,在上述旋转数组的基础上,数组中含有重复数字了。

此题重点是思考:

  • Would allow duplicates affect the run-time complexity? How and why?

试想一个数组 [1,1,1,......,1,0,1,1,1,......1,1],我们如何找到最小的数,复杂度是怎样的?没错,O(N)了。

什么样的数组会造成这种结果呢? 即nums[0]==nums[-1],此时我们二分查找时,无法确定我们在旋转数组的左部还是右部(因为我们判断的依据就是与nums[0]元素比较),二分失效,只能用 `min()` 函数,暴力遍历了。

但是,除此情况以外,题目的复杂度仍是O(logn),需要注意的是最小的元素可能有多个,写判断条件时需要注意。代码如下:

class Solution:
    def findMin(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        l = len(nums)
        if l == 0:
            return False
        elif l == 1:
            return nums[0]
        min_ = nums[0]
        if nums[0] == nums[l-1]:  # 此时只能O(n)解决
            for i in range(l-1):
                if nums[i] < min_:
                    min_ = nums[i]
            return min_        
        
        elif nums[0] < nums[l-1]:  # 数组没有旋转
            return nums[0]
            
        left, right = 0, l-1
        while left <= right:
            mid = (left + right) // 2
            if mid+1 < l and nums[mid] > nums[mid+1]:
                return nums[mid+1]
            elif nums[0] <= nums[mid]:
                left = mid+1
            else:
                right = mid-1
        return nums[0]

81题在此基础上延伸,不再解析。

关于旋转数组的题目总结到此~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值