[特殊字符] 旋转排序数组中的高效搜索:从线性到二分查找的进阶之路

给定一个由不同元素构成的旋转排序数组(原本是升序排列,但在某个未知点进行了旋转),要求快速找到目标元素的索引。如果不存在,则返回 -1。

示例 1:

  • 输入:arr = [5, 6, 7, 8, 9, 10, 1, 2, 3], key = 3
  • 输出:8

示例 2:

  • 输入:arr = [3, 5, 1, 2], key = 6
  • 输出:-1

示例 3:

  • 输入:arr = [33, 42, 72, 99], key = 42
  • 输出:1

目录

  1. [朴素解法:线性搜索 - O(n) 时间 & O(1) 空间](#朴素解法:线性搜索 - O(n) 时间 & O(1) 空间)
  2. [期望解法一:两次二分查找 - O(log n) 时间 & O(1) 空间](#期望解法一:两次二分查找 - O(log n) 时间 & O(1) 空间)
  3. [期望解法二:单次二分查找 - O(log n) 时间 & O(1) 空间](#期望解法二:单次二分查找 - O(log n) 时间 & O(1) 空间)

1. 朴素解法:线性搜索

最直接的方式就是遍历整个数组,逐个元素与目标值比较。找到则返回索引,否则返回 -1。

复杂度分析:

  • 时间复杂度: O(n) ------ 最坏情况下需要检查所有元素。
  • 空间复杂度: O(1) ------ 只用了常数个变量。

代码示例(Python):

python 复制代码
def search(arr, key):
    for i in range(len(arr)):
        if arr[i] == key:
            return i
    return -1

虽然简单,但效率不高。当数组很大时,线性搜索会非常慢。接下来我们看看如何利用旋转排序数组的特性来加速。


说到旋转排序数组的二分查找,很多人卡在"如何判断哪半有序"这个点上。死磕代码不如亲眼看到指针如何移动------强烈安利一个叫图码的网站,它把60多种算法做成交互式动画,你可以自己输入测试数据,甚至上传C/C++/Java/Python代码,看着代码一行行跑出动画。这个工具专门为408考研和数据结构期末考试设计,全书级知识点+可运行代码,遇到不懂的还能7x24小时选中代码让AI解释。学算法可视化,上图码就对了。

图码-数据结构与算法交互式可视化平台

访问网站:https://totuma.cn

2. 期望解法一:两次二分查找

核心思路是:先找到数组中**最小元素(即旋转点)**的索引,这样就把原数组分成了两个有序的子数组。然后根据目标值与第一个元素的大小关系,决定在哪个子数组上进行标准的二分查找。

步骤详解:

  1. 找到最小元素的索引 pivot(旋转点)。
  2. 如果 arr[pivot] == key,直接返回 pivot
  3. 如果 pivot == 0,说明整个数组是未旋转的,直接对整个数组做二分查找。
  4. 否则,比较 keyarr[0]
    • key >= arr[0],则在左半部分 [0, pivot-1] 进行二分查找。
    • 否则,在右半部分 [pivot+1, n-1] 进行二分查找。

复杂度分析:

  • 时间复杂度: O(log n) ------ 两次二分查找,每次都是 O(log n)。
  • 空间复杂度: O(1) ------ 只用了常数个变量。

代码示例(Python):

python 复制代码
def binarySearch(arr, lo, hi, x):
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if arr[mid] == x:
            return mid
        if arr[mid] < x:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1

def findPivot(arr, lo, hi):
    while lo <= hi:
        if arr[lo] <= arr[hi]:
            return lo
        mid = (lo + hi) // 2
        if arr[mid] > arr[hi]:
            lo = mid + 1
        else:
            hi = mid
    return lo

def search(arr, key):
    n = len(arr)
    pivot = findPivot(arr, 0, n - 1)
    if arr[pivot] == key:
        return pivot
    if pivot == 0:
        return binarySearch(arr, 0, n - 1, key)
    if arr[0] <= key:
        return binarySearch(arr, 0, pivot - 1, key)
    return binarySearch(arr, pivot + 1, n - 1, key)

输出: 8


3. 期望解法二:单次二分查找

更优雅的做法是直接对旋转数组进行一次修改过的二分查找 。在每一次迭代中,我们检查中间元素 arr[mid] 是否等于目标值。如果不是,我们就判断左半部分还是右半部分是有序的,然后根据目标值是否落在该有序区间内来调整搜索范围。

算法流程:

  1. 初始化 lo = 0, hi = n-1
  2. lo <= hi 时:
    • 计算 mid = lo + (hi - lo) // 2
    • 如果 arr[mid] == key,返回 mid
    • 判断左半部分是否有序:arr[mid] >= arr[lo]
      • 如果左半部分有序且 key[arr[lo], arr[mid]) 范围内,则 hi = mid - 1,否则 lo = mid + 1
    • 否则(右半部分有序):
      • 如果 key(arr[mid], arr[hi]] 范围内,则 lo = mid + 1,否则 hi = mid - 1
  3. 如果循环结束未找到,返回 -1。

复杂度分析:

  • 时间复杂度: O(log n) ------ 每次迭代将搜索范围缩小一半。
  • 空间复杂度: O(1) ------ 只用了常数个变量。

代码示例(Python):

python 复制代码
def search(arr, key):
    lo, hi = 0, len(arr) - 1
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if arr[mid] == key:
            return mid
        # 左半部分有序
        if arr[mid] >= arr[lo]:
            if key >= arr[lo] and key < arr[mid]:
                hi = mid - 1
            else:
                lo = mid + 1
        # 右半部分有序
        else:
            if key > arr[mid] and key <= arr[hi]:
                lo = mid + 1
            else:
                hi = mid - 1
    return -1

输出: 8


总结

解法 时间复杂度 空间复杂度 说明
线性搜索 O(n) O(1) 简单但慢
两次二分 O(log n) O(1) 先找旋转点,再二分
单次二分 O(log n) O(1) 直接修改二分查找逻辑

推荐使用单次二分查找,因为它代码更简洁,且不需要额外寻找旋转点。

掌握旋转排序数组的搜索,不仅能应对面试题,更能加深对二分查找变体的理解。下次遇到类似问题,记得试试这个技巧哦!

相关推荐
JieE2121 小时前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
Jack209 小时前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法
小小杨树11 小时前
读懂色彩:拍照调色不再难
算法·计算机视觉·配色
JieE2121 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2121 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术1 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦1 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
用户497863050731 天前
(一)小红的数组操作
算法·编程语言
怕浪猫2 天前
Electron 系列文章封面图
算法·架构·前端框架