力扣33-搜索旋转排序数组

33. 搜索旋转排序数组 - 力扣(LeetCode)

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

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= 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 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 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

提示:

  • 1 <= nums.length <= 5000
  • -104 <= nums[i] <= 104
  • nums 中的每个值都 独一无二
  • 题目数据保证 nums 在预先未知的某个下标上进行了旋转
  • -104 <= target <= 104

由于是旋转数组,所以具备以下性质:

任意取 nums 中的某个数 x 与 nums[n - 1] 比大小:

  • 如果 x > nums[n - 1],说明:nums 有两个递增段,且第一个递增段的所有元素均大于第二个递增段的所有元素,且 x 在第一个递增段
  • 如果 x ≤ nums[n - 1],那么 x 在第二个递增段,或者 nums 本身是递增数组,只有一段

设 x = nums[mid] 是我们要二分的数,要判断 x 和 target 的位置关系,将 x 和 target 这两个数与 nums[n - 1] 比大小:

  • 如果 target > nums[n - 1] ≥ x,则 target 在第一段,x 在第二段,target 在 x 左侧
  • 如果 x > nums[n - 1] ≥ target,则 x 在第一段,target 在第二段,target 在 x 右侧
  • 否则,x 和 target 在同一段,直接比较 x 与 target 的大小即可知谁在左边、谁在右边

知道了谁在左、谁在右,就能进行二分了

python 复制代码
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left, right = -1, len(nums) - 1
        while left + 1 < right:
            mid = (left + right) // 2
            x = nums[mid]
            if target > nums[-1] >= x:
                # target 在第一段,x 在第二段
                right = mid
            elif x > nums[-1] >= target:
                # target 在第二段,x 在第一段
                left = mid
            elif x >= target:
                # x 和 target 在同一段且 x 在 target 右侧
                right = mid
            else:
                left = mid
        return right if nums[right] == target else -1

什么时候是 left < right,什么时候是 left <= right,什么时候是 left + 1 < right?

做二分,搞清楚三个问题:

1.维护的区间是什么?

2.mid要不要保留?

3.区间是不是真的缩小了?

如果这题是 left 初始为 0 ,while 循环的条件是 left < right,那么可能会出现死循环,例如 left = 3,right = 4,那么 mid = 3,然后如果走了第二个分支,那么 mid 会赋值 给 left,那区间就没有缩小,就死循环了

那能不能 left = mid + 1?可以,但是不能同时 right = mid - 1,即 elif nums[mid] >= target 分支中不能写成 right = mid - 1,但 if target > nums[-1] >= nums[mid] 分支中就可以写 right = mid - 1,因为 elif 分支相当于 nums[mid] == target or nums[mid] > target,即 nums[mid] 是有可能成为答案的,如果你 right = mid - 1,那就把答案排除在区间之外了

所以也可以:

python 复制代码
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1
        while left < right:
            mid = (left + right) // 2
            x = nums[mid]
            if target > nums[-1] >= x:
                # target 在第一段,x 在第二段
                right = mid - 1
            elif x > nums[-1] >= target:
                # target 在第二段,x 在第一段
                left = mid + 1
            elif x >= target:
                # x 和 target 在同一段且 x 在 target 右侧
                right = mid
            else:
                left = mid + 1
        return right if nums[right] == target else -1

处理输入输出版:

cases.txt:

复制代码
[4,5,6,7,0,1,2]
0
[4,5,6,7,0,1,2]
3
[1]
0

Python

python 复制代码
from typing import List
import ast

def search(nums: List[int], target: int) -> int:
    left, right = -1, len(nums) - 1
    while left + 1 < right:
        mid = (left + right) // 2
        x = nums[mid]
        if target > nums[-1] >= x:
            # target 在第一段,x 在第二段
            right = mid
        elif x > nums[-1] >= target:
            # x 在第一段,target 在第二段
            left = mid
        elif x >= target:
            # x 与 target 在同一段且 x 在 target 右侧
            right = mid
        else:
            # x 与 target 在同一段且 x 在 target 左侧
            left = mid
    return right if nums[right] == target else -1

def read_cases(filename: str):
    cases = []
    with open(filename, "r") as f:
        lines = [
            line.strip() for line in f
        ]

    for i in range(0, len(lines), 2):
        nums = ast.literal_eval(
            lines[i]
        )
        target = ast.literal_eval(
            lines[i + 1]
        )
        cases.append(
            (nums, target)
        )
    return cases

def main():
    cases = read_cases("cases.txt")
    for nums, target in cases:
        res = search(nums, target)
        print(f"nums = {nums}")
        print(f"target = {target}")
        print(f"result = {res}")
        print()

if __name__ == "__main__":
    main()
相关推荐
念何架构之路4 分钟前
zap采样器与性能优化内幕
算法·性能优化·哈希算法
晒太羊的猫1 小时前
hot100动态规划刷题
算法·动态规划
weixin_531094691 小时前
heap_4内存管理
java·开发语言·算法
余额瞒着我当琳1 小时前
红黑树的实现--红黑树的概念、规则、效率、结构、旋转、查找、验证
数据结构·c++·算法
程序员南飞2 小时前
win系统安装minio
java·数据结构·算法
青少儿编程课堂2 小时前
AC自动机多模式串匹配精讲:校园关键词巡检实战解析
c++·python·算法·bfs·信息学竞赛
我命由我123452 小时前
Photoshop - Photoshop 矢量蒙版
学习·职场和发展·产品运营·求职招聘·职场发展·产品经理·photoshop
longlongzihan2 小时前
LeetCode 5. 最长回文子串:从暴力匹配到中心扩展的思维演进
c++·算法·leetcode
waoooqwe2 小时前
问卷样本真实吗
大数据·数据库·算法·数据分析
苦瓜打怪兽2 小时前
leetcode 热题100(11. 盛最多水的容器)
python·算法·leetcode