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

《 java 随想录》| 数组

前言:这是专门针对java语言讲解的算法解析(题目顺序参考代码随想录)

思维导图

704. 二分查找


一、核心逻辑:缩小查找区间

数组是升序排列的,这意味着:

  • 如果某元素大于目标值,那么目标值一定在该元素的左侧;
  • 如果某元素小于目标值,那么目标值一定在该元素的右侧。

利用这个特性,我们可以每次选取区间中间的元素与目标值比较,从而将查找区间缩小一半,直到找到目标值或确认其不存在。

二、关键细节:区间定义与循环不变量

二分法的难点在于边界处理,而边界处理的核心是明确区间的定义。只要在整个查找过程中严格遵守区间的定义(即 “循环不变量”),就能避免逻辑混乱。

解法:左闭右闭区间 [left, right]

  • 区间含义:查找的范围是从 left 到 right,且 left 和 right 对应的元素都可能是目标值。
  • 循环条件while (left <= right)
    因为当 left == right 时,区间 [left, right] 仍有一个元素需要检查,所以循环继续。
  • 边界调整
    • 若 nums[middle] > target:目标值一定在 middle 左侧,因此右边界调整为 right = middle - 1(排除 middle)。
    • 若 nums[middle] < target:目标值一定在 middle 右侧,因此左边界调整为 left = middle + 1(排除 middle)。
    • 若 nums[middle] == target:直接返回 middle(找到目标)。
  • 未找到的情况:当循环结束(left > right),说明整个区间已排查完毕,返回 -1

三、代码示例:

class Solution {public int search(int[] nums, int target) {// 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算if (target < nums[0] || target > nums[nums.length - 1]) {return -1;}int left = 0, right = nums.length - 1;while (left <= right) {int mid = left + ((right - left) >> 1);if (nums[mid] == target) {return mid;}else if (nums[mid] < target) {left = mid + 1;}else { // nums[mid] > targetright = mid - 1;}}// 未找到目标值return -1;}
}

27. 移除元素


一、核心逻辑:覆盖而非删除

数组的元素在内存中是连续排列的,无法直接删除某个元素,只能用后续元素覆盖掉目标元素。因此,解题的关键在于:如何高效地将不需要删除的元素移动到数组前面,并确定新数组的长度

二、暴力解法:两层循环逐个覆盖

思路:遍历数组,每遇到一个等于目标值的元素,就将其后所有元素向前移动一位。

  1. 外层循环:遍历数组中的每个元素。
  2. 发现目标值:当遇到等于 val 的元素时,执行内层循环。
  3. 内层循环:将该元素之后的所有元素向前移动一位(覆盖掉当前元素)。
  4. 调整索引和长度:由于元素前移,当前索引 i 需要减 1(回退一位),同时数组长度减 1。

时间复杂度:O(n2),因为每次删除元素需要移动后续所有元素。
空间复杂度:O(1),仅使用常数额外空间。

三、双指针法:高效覆盖(推荐)

核心思想:使用快慢指针在一次遍历中完成元素筛选和数组重构。

双指针定义

  • 慢指针:指向新数组中下一个元素的位置(即待填充位置)。
  • 快指针:遍历原数组,寻找所有不等于 val 的元素。

步骤

  1. 初始化指针slow 和 fast 均从 0 开始。
  2. 遍历数组:快指针 fast 逐个检查元素。
  3. 筛选元素:若 nums[fast] != val,将其复制到 nums[slow],并将 slow 后移一位。
  4. 返回结果:遍历结束后,slow 的值即为新数组的长度。

时间复杂度:O(n),只需一次遍历。
空间复杂度:O(1),仅使用常数额外空间。

四. 代码示例

class Solution {public int removeElement(int[] nums, int val) {// 快慢指针int slowIndex = 0;for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {if (nums[fastIndex] != val) {nums[slowIndex] = nums[fastIndex];slowIndex++;}}return slowIndex;}
}

977. 有序数组平方和


一、核心逻辑:平方后的最大值在两端

由于原数组按非递减顺序排列,负数部分平方后可能成为较大值,但负数的绝对值越大,平方后的值越大。因此,平方后的最大值一定出现在原数组的两端(最左边或最右边),而不是中间。

二、暴力解法:平方后排序

思路:遍历数组,将每个元素平方,然后对整个数组进行排序。

步骤

  1. 遍历数组:将每个元素替换为其平方值。
  2. 排序:对平方后的数组进行排序(如快速排序)。

示例: 原数组 [-4,-1,0,3,10] → 平方后 [16,1,0,9,100] → 排序后 [0,1,9,16,100]

三、双指针法:高效生成有序数组(推荐)

核心思想:利用原数组两端元素平方后的最大值特性,使用双指针从两端向中间遍历,依次填充新数组的最大值。

双指针定义

  • 左指针 i:指向原数组的起始位置(最小元素)。
  • 右指针 j:指向原数组的末尾位置(最大元素)。
  • 结果数组指针 k:指向结果数组的末尾位置(用于填充最大值)。

步骤

  1. 初始化指针i = 0j = 数组长度-1k = 数组长度-1
  2. 比较平方值:比较 nums[i]² 和 nums[j]² 的大小:
    • 若 nums[i]² < nums[j]²:将 nums[j]² 放入结果数组的 k 位置,j 左移一位,k 左移一位。
    • 否则:将 nums[i]² 放入结果数组的 k 位置,i 右移一位,k 左移一位。
  3. 循环条件:重复步骤 2,直到 i > j(所有元素处理完毕)。

四. 代码示例

class Solution {public int[] sortedSquares(int[] nums) {int right = nums.length - 1;int left = 0;int[] result = new int[nums.length];int index = result.length - 1;while (left <= right) {if (nums[left] * nums[left] > nums[right] * nums[right]) {// 正数的相对位置是不变的, 需要调整的是负数平方后的相对位置result[index--] = nums[left] * nums[left];++left;} else {result[index--] = nums[right] * nums[right];--right;}}return result;}
}

209. 长度最小的子数组


一、核心逻辑:滑动窗口找最小长度

由于数组元素均为正整数(根据题目隐含条件,若存在负数需额外处理,但该解法默认元素为正),当子数组和大于等于目标值时,缩短窗口左侧可尝试找到更短的有效子数组。因此,可通过滑动窗口(双指针)动态调整子数组范围,高效计算满足条件的最小长度。

二、滑动窗口解法:双指针动态调整

思路:使用快指针扩展窗口右边界,累计子数组和;当和大于等于目标值时,移动慢指针收缩左边界,更新最小长度,同时减少累计和。

双指针定义:

  • 慢指针 slow:指向当前子数组的起始位置,用于收缩窗口。
  • 快指针 fast:指向当前子数组的结束位置,用于扩展窗口。
  • 辅助变量 sum:记录当前窗口(子数组)的元素和;len:记录满足条件的子数组最小长度(初始化为最大值)。

步骤:

  1. 初始化指针与变量:slow = 0sum = 0len = Integer.MAX_VALUE
  2. 扩展窗口:快指针 fast 从 0 开始遍历数组,每次将 nums[fast] 加入 sum
  3. 收缩窗口:当 sum >= target 时,执行以下操作:
    • 计算当前窗口长度(fast - slow + 1),与 len 比较并更新最小值。
    • 将 nums[slow] 从 sum 中减去,慢指针 slow 右移一位,缩小窗口范围。
    • 重复收缩操作,直到 sum < target 为止。
  4. 结果处理:遍历结束后,若 len 仍为初始最大值(无满足条件的子数组),返回 0;否则返回 len

public int minSubArrayLen(int target, int[] nums) {int len = Integer.MAX_VALUE;int slow = 0;int fast;int sum = 0;for (fast = 0; fast < nums.length; fast++) {sum += nums[fast];while (sum >= target) {sum -= nums[slow];len = Math.min(fast - slow + 1, len);slow++;}}return len == Integer.MAX_VALUE ? 0 : len;}

至此,大功告成!🎉🎉🎉

http://www.lryc.cn/news/599050.html

相关文章:

  • ollama无法拉取模型导致报错
  • Java并发编程第八篇(CountDownLatch组件分析)
  • Python Day15 面向对象核心特性笔记 及 例题分析
  • 深度学习(鱼书)day01--感知机
  • 基于CloudBase+React+CodeBudddy的云上智能睡眠应用开发实践
  • Rust与YOLO目标检测实战
  • rust-结构体使用示例
  • 论文阅读:《无约束多目标优化的遗传算法,群体和进化计算》
  • Eureka-服务注册,服务发现
  • SpringBoot航空订票系统的设计与实现
  • 华为OpenStack架构学习9篇 连载—— 01 OpenStack架构介绍【附全文阅读】
  • docker pull weaviate 国内拉取失败的问题
  • java中如何返回一个可以执行返回操作(return action)的函数或对象
  • rust-枚举
  • 技术赋能多元探索:我的技术成长与行业洞察
  • 【安卓笔记】lifecycle与viewModel
  • MySQL的底层原理--InnoDB记录存储结构
  • Ollama(5)服务接口压力测试
  • PostgreSQL 保留关键字冲突问题:语法错误 在 “user“ 或附近的 LINE 1: CREATE TABLE user
  • Windchill用SQL获取所有组织下的所有用户
  • CIRL:因果启发的表征学习框架——从域泛化到奖励分解的因果革命
  • Linux进程间通信:管道机制全方位解读
  • 【MediaTek】AN7563编译wlan_hwifi出现en_npu.c:42:10: fatal error:
  • 【STM32项目】水质检测
  • 【数组的定义与使用】
  • 使用Python采集招聘网站数据并智能分析求职信息
  • AI大模型各类概念扫盲
  • 【C++】标准模板库(STL)—— 学习算法的利器
  • 算法题(179):单调栈
  • C++抽象类完全指南