(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

相关推荐
颜酱1 小时前
图的数据结构:从「多叉树」到存储与遍历
javascript·后端·算法
架构师沉默6 小时前
别又牛逼了!AI 写 Java 代码真的行吗?
java·后端·架构
zone77397 小时前
006:RAG 入门-面试官问你,RAG 为什么要切块?
后端·算法·面试
CoovallyAIHub9 小时前
OpenClaw 近 2000 个 Skills,为什么没有一个好用的视觉检测工具?
深度学习·算法·计算机视觉
CoovallyAIHub9 小时前
CVPR 2026 | 用一句话告诉 AI 分割什么——MedCLIPSeg 让医学图像分割不再需要海量标注
深度学习·算法·计算机视觉
CoovallyAIHub10 小时前
Claude Code 突然变成了 66 个专家?这个 5.8k Star 的开源项目,让我重新理解了什么叫"会用 AI"
深度学习·算法·计算机视觉
兆子龙10 小时前
前端哨兵模式(Sentinel Pattern):优雅实现无限滚动加载
前端·javascript·算法
后端AI实验室10 小时前
我把一个生产Bug的排查过程,交给AI处理——20分钟后我关掉了它
java·ai
凉年技术12 小时前
Java 实现企业微信扫码登录
java·企业微信
程序员爱钓鱼13 小时前
Go并发控制核心:context 包完整技术解析
后端·google·go