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

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

整数数组 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

你必须设计一个时间复杂度为 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()
相关推荐
地平线开发者21 分钟前
征程6工具链模型X86推理方式说明
算法
地平线开发者25 分钟前
【征程6】校准量化中HistogramObserver解析
算法
OuO-21 小时前
笔试强训 Day 34:ISBN 号码、kotori 和迷宫、矩阵最长递增路径
java·算法·矩阵
程序喵大人2 小时前
【C++进阶】STL算法与函数对象 - 04 find、count和any_of把查询写成意图
开发语言·c++·算法
豆沙沙包?2 小时前
c++中引用(P7-P11)
java·c++·算法
不会代码的小猴3 小时前
标准模板库(STL)
开发语言·c++·笔记·算法
ZhouDevin3 小时前
算法论文/高效微调4——DoRA:权重分解的低秩适配方法
算法
evans在进步3 小时前
LeetCode 200:岛屿数量——Java DFS 染色法详解
java·leetcode·深度优先
AI探索先锋3 小时前
A* 路径规划:四种算法的进化史-学习
学习·算法
旖旎夜光3 小时前
LeetCode 202:快乐数(双指针问题) —— 题解
数据结构·c++·算法·leetcode·双指针