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

问题简介

🔗 LeetCode 33. 搜索旋转排序数组

题目描述

整数数组 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. 初始化 left = 0, right = nums.length - 1
  2. left <= right
    • 计算中点 mid = (left + right) / 2
    • 如果 nums[mid] == target,直接返回 mid
    • 判断左半部分是否有序(即 nums[left] <= nums[mid]):
      • 如果是,则检查 target 是否在 [nums[left], nums[mid]) 范围内:
        • 若在,缩小右边界:right = mid - 1
        • 否则,搜索右半部分:left = mid + 1
      • 如果左半部分无序,则右半部分一定有序:
        • 检查 target 是否在 (nums[mid], nums[right]] 范围内:
          • 若在,left = mid + 1
          • 否则,right = mid - 1
  3. 循环结束仍未找到,返回 -1

💡 关键点:

  • 由于数组元素互不相同,nums[left] == nums[mid] 只有在 left == mid 时成立,此时仍可视为左半有序。
  • 每次都能排除一半的搜索空间,保证 O(log n) 时间复杂度。
❌ 方法二:线性扫描(不满足题目要求)
  • 直接遍历数组,时间复杂度 O(n),不符合 O(log n) 要求,仅作对比。
💡 方法三:先找旋转点再二分(可行但略复杂)
  1. 先用二分查找找到最小值(即旋转点)的位置 pivot
  2. 判断 target 应该在前半段还是后半段。
  3. 在对应段内进行标准二分查找。

虽然也是 O(log n),但需要两次二分,代码更复杂,不如方法一直接。


代码实现

Java Go

java 复制代码
复制代码
class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            
            if (nums[mid] == target) {
                return mid;
            }
            
            // 左半部分有序
            if (nums[left] <= nums[mid]) {
                if (target >= nums[left] && target < nums[mid]) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            } 
            // 右半部分有序
            else {
                if (target > nums[mid] && target <= nums[right]) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
        }
        
        return -1;
}
go 复制代码
复制代码
func search(nums []int, target int) int {
    left, right := 0, len(nums)-1
    for left <= right {
        mid := left + (right-left)/2
        
        if nums[mid] == target {
            return mid
        }
        
        // 左半部分有序
        if nums[left] <= nums[mid] {
            if target >= nums[left] && target < nums[mid] {
                right = mid - 1
            } else {
                left = mid + 1
            }
        } else {
            // 右半部分有序
            if target > nums[mid] && target <= nums[right] {
                left = mid + 1
            } else {
                right = mid - 1
            }
        }
    }
    return -1
}

示例演示

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

步骤 left right mid nums[mid] 有序部分 target 范围判断 新边界
1 0 6 3 7 左有序 0 ∉ [4,7) left=4
2 4 6 5 1 右有序 0 ∈ (1,2]? 否 right=4
3 4 4 4 0 --- 找到! return 4

✅ 成功找到目标值。


答案有效性证明

  • 正确性 :每次迭代都基于"至少有一半是有序的"这一性质,通过判断 target 是否落在有序区间内来决定搜索方向,逻辑严密。
  • 终止性 :每次循环要么找到目标,要么缩小搜索区间(leftright 移动),最终 left > right 退出。
  • 覆盖边界 :包括单元素、未旋转(k=0)、target 在旋转点等边界情况均能处理。

复杂度分析

项目 复杂度
✅ 时间复杂度 O(log n) ------ 每次排除一半元素
✅ 空间复杂度 O(1) ------ 仅使用常数额外空间

问题总结

📌 关键收获:

  • 旋转排序数组虽整体无序,但局部有序,可结合二分查找。
  • 判断哪一半有序是解题突破口。
  • 条件判断需严谨,注意边界(如 <= vs <)。

💡 扩展思考:

  • 若数组允许重复元素(如 LeetCode 81 题),则 nums[left] == nums[mid] 时无法判断哪边有序,需特殊处理(如 left++)。
  • 本题是"在部分有序结构中应用二分查找"的经典范例,对理解二分思想的灵活性很有帮助。

✅ 掌握此题,就掌握了处理"旋转数组"类问题的核心技巧!

github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions

相关推荐
untE EADO3 分钟前
Tomcat的server.xml配置详解
xml·java·tomcat
ictI CABL12 分钟前
Tomcat 乱码问题彻底解决
java·tomcat
敖正炀15 分钟前
DelayQueue 详解
java
敖正炀41 分钟前
PriorityBlockingQueue 详解
java
shark22222221 小时前
Spring 的三种注入方式?
java·数据库·spring
workflower1 小时前
机器人应用-楼宇室内巡逻
大数据·人工智能·算法·microsoft·机器人·动态规划·享元模式
ZPC82101 小时前
fanuc 机器人通过PR寄存器实现轨迹控制
人工智能·算法·计算机视觉·机器人
陈煜的博客1 小时前
idea 项目只编译不打包,跳过测试,快速开发
java·ide·intellij-idea
py有趣1 小时前
力扣热门100题之编辑距离
数据结构·算法·leetcode
JAVA学习通1 小时前
LangChain4j 与 Spring AI 的技术选型深度对比:2026 年 Java AI 工程化实践指南
java·人工智能·spring