当前位置: 首页 > news >正文

LeetCode 33 搜索旋转排序数组

攻克 “搜索旋转排序数组” 问题:Java 实战解析

在算法与数据结构的探索旅程中,我们常常会遇到各种极具挑战性的问题。“搜索旋转排序数组” 便是其中之一,它巧妙地结合了数组的排序特性与旋转操作,对我们的逻辑思维和编程能力提出了较高要求。通过深入研究并解决这个问题,我们不仅能提升对 Java 语言的运用熟练度,更能深入理解如何在复杂情境下设计高效的算法。

一、问题描述

我们面对的是一个整数数组 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) 的算法来解决此问题。

二、问题分析

2.1 数组特性分析

旋转后的数组虽然整体不再是严格的升序,但它具有一个关键特性:可以看作由两个有序的子数组拼接而成。比如 [4,5,6,7,0,1,2],前半部分 [4,5,6,7] 是有序的,后半部分 [0,1,2] 也是有序的。这个特性是我们解决问题的关键突破口,因为它让我们有可能利用类似二分查找的思想来快速定位目标值。

2.2 二分查找的适用性

我们知道二分查找在有序数组中能以 O(log n) 的时间复杂度高效地查找目标值。对于旋转排序数组,我们可以尝试在每次迭代中,通过判断中间元素与两端元素的关系,确定中间元素位于哪个有序子数组中,进而判断目标值可能存在的区间,从而缩小查找范围,实现类似二分查找的效果。

2.3 边界条件处理

在进行二分查找的过程中,需要仔细处理边界条件。例如,当数组长度为 1 时,直接判断该元素是否为目标值即可。当数组长度大于 1 时,要注意在更新左右指针时,确保不会越界,并且能正确地根据中间元素与目标值的比较结果来调整查找区间。

三、代码实现

class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int 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 (nums[left] <= target && target < nums[mid]) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            } 
            // 右半部分有序
            else {
                if (nums[mid] < target && target <= nums[right]) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
        }

        return -1;
    }
}

四、代码说明

4.1 初始化指针

left 指针初始化为 0,指向数组的起始位置;right 指针初始化为 nums.length - 1,指向数组的末尾位置。这两个指针用于界定当前的查找区间。

4.2 二分查找循环

通过 while (left <= right) 循环,不断缩小查找区间,直到找到目标值或者确定目标值不存在。在每次循环中,计算中间位置 mid,使用 left + (right - left) / 2 的方式可以避免 left + right 可能产生的溢出问题。

4.3 目标值判断

如果 nums[mid] 等于 target,说明找到了目标值,直接返回 mid

4.4 左半部分有序情况

当 nums[left] <= nums[mid] 时,表明左半部分数组是有序的。此时,如果 nums[left] <= target && target < nums[mid],说明目标值在左半部分有序区间内,将 right 更新为 mid - 1,缩小查找区间到左半部分;否则,将 left 更新为 mid + 1,将查找区间移动到右半部分。

4.5 右半部分有序情况

当 nums[left] > nums[mid] 时,说明右半部分数组是有序的。如果 nums[mid] < target && target <= nums[right],说明目标值在右半部分有序区间内,将 left 更新为 mid + 1;否则,将 right 更新为 mid - 1,缩小查找区间到左半部分。

4.6 未找到目标值

如果循环结束后仍未找到目标值,说明目标值不存在于数组中,返回 -1

五、时间和空间复杂度分析

5.1 时间复杂度

在整个查找过程中,每次迭代都将查找区间缩小一半,类似于二分查找。因此,时间复杂度为 O(log n),其中 n 是数组 nums 的长度。这满足题目中对时间复杂度的要求。

5.2 空间复杂度

代码中除了输入的数组 nums 外,只使用了几个额外的变量,如 leftrightmid 等,这些变量占用的空间是常数级别的。因此,空间复杂度为 O(1)

通过对 “搜索旋转排序数组” 问题的深入剖析与 Java 代码实现,我们成功地设计出了高效的算法,不仅解决了问题,还深入理解了时间和空间复杂度的优化。希望这篇文章能为大家在算法学习和实际编程中提供有益的借鉴和帮助。

http://www.dtcms.com/a/122837.html

相关文章:

  • Tailwind CSS的五节课教学计划
  • 动态科技感html导航网站源码
  • MySQL:事务
  • VectorBT量化入门系列:第四章 高级策略开发与优化
  • Rust Command无法执行*拓展解决办法
  • 在线PDF文件拆分工具,小白工具功能实用操作简单,无需安装的文档处理工具
  • 基金的分类与如何选择基金
  • Quantz框架学习
  • Kafka 如何保证消息有序性?
  • Java 面向对象(构造类、对象)
  • 【系统架构设计师】数据库系统 ⑤ ( 数据库设计过程 - 逻辑设计 | ER 图 转为 关系模式 | 实体 转 关系模式 | 联系 转 关系模式 - 并入实体、独立关系 )
  • 适合工程建筑行业的OA系统有什么推荐?
  • 【后端开发】SpringBoot与Spring MVC
  • Nacos 健康检查是如何实现的?支持哪些健康检查协议?
  • AI搜索+法律咨询:在「事实重构」与「程序正义」的博弈场‌
  • c#的form实现叠叠乐游戏
  • Git 中回退版本后修改并提交
  • HarmonyOS Next~鸿蒙系统原生流畅性创新解析:预加载技术与全栈优化的革命性突破
  • Docker中Redis修改密码失效
  • ISIS单区域抓包分析
  • 常微分方程求解全解析:从基础到矩阵方法深度实践
  • Vue 3 + Element Plus 快速入门教程
  • ansible 实现达梦7数据库初始化数据脚本写入
  • docker使用
  • 2025年项目管理工具TOP10:Gitee引领技术驱动新浪潮
  • 【 C# 使用 MiniExcel 库的典型场景】
  • 开源免费虚拟化平台PVE软件定义网络
  • BGP路由协议之对等体
  • Containerd介绍
  • C++中如何在一个字符串中的任何一个位置插入字符或者字符串--insert()函数实现