ACM 选手图解 LeetCode 搜索旋转排序数组

简介: ACM 选手图解 LeetCode 搜索旋转排序数组

大家好呀,我是旋转蛋。


今天解决搜索旋转排序数组,用二分查找解决局部有序数组的经典问题。


话不多说,让我们来会一会它。

640.png


   LeetCode 33:搜索旋转排序数组



题意


整数数组 nums 升序排列且无重复元素。


给你从某个下标旋转后的数组 nums 和 target,如果 target 在 nums 中返回下标,否则返回 -1。


示例


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

输出:4

解释:原 nums = [0,1,2,4,5,6,7],从下标 3 旋转后变为现在的 nums = [4,5,6,7,0,1,2]


提示


数据保证 nums 在预先未知的某个下标上进行了旋转,nums 中的每个值独一无二。


  • 1 <= len(nums) <= 5000
  • -10^4 <= nums[i]、target <= 10^4

题目解析


又是一种新的题型,难度中等。


我们早就知道,二分查找必须的条件是数组有序。


刚看这题题意第一行的时候,数数组 nums 升序排列且无重复元,当时我就默默的掏出了我的二分查找板子。


辣鸡,写的这么明显,当我是傻子嘛。

640.jpg

然后还没开心到高潮,发现 nums 被人扭了一下,旋转了。


小丑竟是我自己...


出题人你出来,你当数组是尼玛魔方嘛,想扭就扭。

640.jpg


不过再定睛一看,这题二分查找还是可解。


因为数组被旋转以后,总有一部分是有序的


比如 [4,5,6,7,0,1,2] 中,我找到 mid,不管是 0 ~ mid 或者 mid ~ n-1,总有半拉子是有序的,我们在有序的那边进行二分查找是可以的。


那这样二分查找稍微变一下,加个有序的判断,这道题的解法就出来了:


  • 找出 mid,如果 nums[mid] == target,直接返回。
  • 如果 [low,mid-1] 有序:
  • target 在 [nums[low],nums[mid]] 中,范围缩小至 [low,mid-1]。
  • target 不在 [nums[low],nums[mid]] 中,范围缩小至 [mid+1,high]。
  • 如果 [mid+1,high] 有序:
  • target 在 [nums[mid+1],nums[high]] 中,范围缩小至 [mid+1,high]。
  • target 不在 [nums[mid+1],nums[high]] 中,范围缩小至 [low,mid-1]。

下面我们来看图解。


图解


nums = [4,5,6,7,0,1,2], target = 0 为例。


首先初始化 low 和 high 指针。

640.png

low, high = 0, len(nums) - 1

第 1 步,low = 0,high = 6,mid = low + (high - low) // 2 = 3:

640.png

mid = low + (high - low) // 2


此时 nums[low] < nums[mid],且 target 不在左区间内,所以 low 向右移动至 mid + 1 = 4。

640.png

# 如果左区间有序
if nums[low] <= nums[mid]:
    # target 在左区间
    if nums[low] <= target < nums[mid]:
        high = mid - 1
    # target 在右区间
    else:
        low = mid + 1


第 2 步,low = 4,high = 6,mid = low + (high - low) // 2 = 5:

640.png


此时 nums[low] < nums[mid],且 target 在左区间内,所以 high 向左移动至 mid - 1 = 4。

640.png


第 3 步,low = 4,high = 4,mid = low + (high - low) // 2 = 4:

640.png


此时 nums[mid] == target ,直接返回结果。

# 如果找到,返回结果
if nums[mid] == target:
    return mid


本题解使用二分查找,时间复杂度为 O(n)


同时只额外维护了几个指针,所以空间复杂度为 O(1)


代码实现


Python 代码实现

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if not nums:
            return -1
        low, high = 0, len(nums) - 1
        while low <= high:
            mid = low + (high - low) // 2
            # 如果找到,返回结果
            if nums[mid] == target:
                return mid
            # 如果左区间有序
            if nums[low] <= nums[mid]:
                # target 在左区间
                if nums[low] <= target < nums[mid]:
                    high = mid - 1
                # target 在右区间
                else:
                    low = mid + 1
            # 如果右区间有序
            else:
                # target 在右区间
                if nums[mid] < target <= nums[high]:
                    low = mid + 1
                # target 在左区间
                else:
                    high = mid - 1
        return -1


Java 代码实现

class Solution {
    public int search(int[] nums, int target) {
        int n = nums.length;
        if (n == 0) {
            return -1;
        }
        if (n == 1) {
            return nums[0] == target ? 0 : -1;
        }
        int l = 0, r = n - 1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            if (nums[0] <= nums[mid]) {
                if (nums[0] <= target && target < nums[mid]) {
                    r = mid - 1;
                } 
                else {
                    l = mid + 1;
                }
            } 
            else {
                if (nums[mid] < target && target <= nums[n - 1]) {
                    l = mid + 1;
                } 
                else {
                    r = mid - 1;
                }
            }
        }
        return -1;
    }
}

图解搜索旋转排序数组到这就结束辣,又是一种新的题型,你学废了嘛?

640.jpg


还是那句话,二分查找,只要每次做题小心点,就没啥问题。


如果还有什么问题的话,可以留言区提出来。


记得帮本蛋三连,点赞 + 在看 + 转发


我是帅蛋,我们下次见!



相关文章
|
2天前
|
索引
力扣随机一题 6/26 哈希表 数组 思维
力扣随机一题 6/26 哈希表 数组 思维
5 0
|
2天前
力扣随机一题 哈希表 排序 数组
力扣随机一题 哈希表 排序 数组
6 1
|
2天前
|
算法 索引
力扣随机一题 位运算/滑动窗口/数组
力扣随机一题 位运算/滑动窗口/数组
10 0
|
2天前
|
算法 索引
力扣每日一题 6/28 动态规划/数组
力扣每日一题 6/28 动态规划/数组
5 0
|
2天前
力扣随机一题 6/28 数组/矩阵
力扣随机一题 6/28 数组/矩阵
5 0
|
16天前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-2
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
16天前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-1
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
17天前
|
索引
【LeetCode刷题】二分查找:山脉数组的峰顶索引、寻找峰值
【LeetCode刷题】二分查找:山脉数组的峰顶索引、寻找峰值
|
17天前
|
算法
【LeetCode刷题】滑动窗口解决问题:串联所有单词的子串(困难)、最小覆盖子串(困难)
【LeetCode刷题】滑动窗口解决问题:串联所有单词的子串(困难)、最小覆盖子串(困难)
|
17天前
|
算法 容器
【LeetCode刷题】滑动窗口解决问题:水果成篮、找到字符串中所有字母异位词
【LeetCode刷题】滑动窗口解决问题:水果成篮、找到字符串中所有字母异位词