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

给定一个由不同元素构成的旋转排序数组(原本是升序排列,但在某个未知点进行了旋转),要求快速找到目标元素的索引。如果不存在,则返回 -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) 直接修改二分查找逻辑

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

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

相关推荐
纪念 2293 小时前
顺序表(数据结构入门的开端)
数据结构
汉字萌萌哒3 小时前
2025 CSP-S提高级(第一轮)C++真题以及答案
数据结构·算法
明志数科3 小时前
仿真数据与真实数据:机器人训练的数据策略选择
人工智能·算法·机器学习
weyyhdke3 小时前
2026电源与MCU控制设计实战:用Gemini3.5镜像站免费优化开关电源环路与电机FOC算法硬核教程
单片机·嵌入式硬件·算法
小张成长计划..3 小时前
【C++】35:位图,布隆过滤器和海量数据处理(哈希扩展)
算法·哈希算法
z200509303 小时前
今日算法(组合问题III)(回溯的使用)
java·算法·leetcode
春栀怡铃声3 小时前
【C++修仙录02】筑基篇:list 使用
数据结构·list
2401_889626923 小时前
Java语法进阶篇
算法
Sinsa_SI3 小时前
2026算法应用主题赛初赛-小学4-6组(Python)试卷(含答案+详细解析)
开发语言·python·算法