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

力扣2444. 统计定界子数组的数目:Java三种解法详解


力扣2444. 统计定界子数组的数目:Java三种解法详解

题目描述

给定整数数组 nums 和两个整数 minKmaxK,统计满足以下条件的子数组数目:

  1. 子数组的最小值等于 minK
  2. 子数组的最大值等于 maxK

示例
输入:nums = [1,3,5,2,7,5], minK = 1, maxK = 5
输出:2
解释:满足条件的子数组为 [1,3,5][1,3,5,2]


解法一:单次遍历 + 边界标记(推荐)

代码实现

class Solution {public long countSubarrays(int[] nums, int minK, int maxK) {int n = nums.length;long ans = 0;int minPos = -1, maxPos = -1, border = -1;for (int i = 0; i < n; i++) {// 更新越界标记if (nums[i] < minK || nums[i] > maxK) {border = i;}// 更新最近的最小值和最大值位置if (nums[i] == minK) {minPos = i;}if (nums[i] == maxK) {maxPos = i;}// 计算当前可形成子数组的数量ans += Math.max(0, Math.min(minPos, maxPos) - border);}return ans;}
}

复杂度分析

  • 时间复杂度:O(n),仅需一次遍历数组。
  • 空间复杂度:O(1),仅使用常数变量存储标记。

核心思路

  1. 标记关键边界
    • border:记录最近出现不满足 minK ≤ num ≤ maxK 的元素位置,作为子数组的左边界限制。
    • minPosmaxPos:分别记录最近出现的 minKmaxK 的位置。
  2. 统计逻辑
    • 遍历时,若当前元素是 minKmaxK,更新对应位置。
    • 有效子数组的左端点范围为 (border, min(minPos, maxPos)],每次遍历累加有效子数组数量。

解法二:滑动窗口(双指针)

代码实现

class Solution {public long countSubarrays(int[] nums, int minK, int maxK) {int n = nums.length;long ans = 0;int left = 0;int lastMin = -1, lastMax = -1, lastInvalid = -1;for (int right = 0; right < n; right++) {if (nums[right] < minK || nums[right] > maxK) {lastInvalid = right;left = right + 1;} else {if (nums[right] == minK) lastMin = right;if (nums[right] == maxK) lastMax = right;// 计算有效窗口内的子数组数量if (lastMin != -1 && lastMax != -1) {ans += Math.max(0, Math.min(lastMin, lastMax) - lastInvalid);}}}return ans;}
}

复杂度分析

  • 时间复杂度:O(n),单次遍历数组。
  • 空间复杂度:O(1),仅需常数变量。

核心思路

  1. 滑动窗口维护
    • leftright 定义窗口范围。
    • 当遇到越界元素时,重置窗口起点。
  2. 统计条件
    • 窗口内必须包含至少一个 minKmaxK
    • 有效子数组的数量由最近的有效位置决定。

解法三:暴力优化(预处理位置)

代码实现

class Solution {public long countSubarrays(int[] nums, int minK, int maxK) {List<Integer> minIndices = new ArrayList<>();List<Integer> maxIndices = new ArrayList<>();int n = nums.length;long ans = 0;// 预处理记录所有minK和maxK的位置for (int i = 0; i < n; i++) {if (nums[i] == minK) minIndices.add(i);if (nums[i] == maxK) maxIndices.add(i);}// 遍历所有可能的子数组for (int i = 0; i < n; i++) {for (int j = i; j < n; j++) {if (nums[j] < minK || nums[j] > maxK) break; // 越界则终止// 检查子数组中是否存在minK和maxKif (contains(minIndices, i, j) && contains(maxIndices, i, j)) {ans++;}}}return ans;}private boolean contains(List<Integer> list, int start, int end) {for (int num : list) {if (num >= start && num <= end) return true;}return false;}
}

复杂度分析

  • 时间复杂度:O(n^2),最坏情况下需双重循环。
  • 空间复杂度:O(n),存储预处理的位置列表。

核心思路

  1. 预处理优化
    • 提前记录所有 minKmaxK 的位置。
  2. 暴力枚举优化
    • 遍历所有子数组,若子数组不越界且包含 minKmaxK,则计数。

各解法对比

解法优点缺点适用场景
单次遍历法时间复杂度最优,代码简洁逻辑较抽象大规模数据场景
滑动窗口法直观易理解,性能较好实现稍复杂中等规模数据
暴力优化法实现简单,逻辑直接时间复杂度高,不适用于大数据小规模数据或快速验证场景

示例解析

以输入 nums = [1,3,5,2,7,5], minK = 1, maxK = 5 为例:

  1. 解法一(单次遍历)

    • 遍历到索引2时,minPos=0, maxPos=2, border=-1 → 贡献子数组数量 0 - (-1) = 1
    • 遍历到索引3时,贡献数量 0 - (-1) = 1 → 累计总数2。
  2. 解法二(滑动窗口)

    • 窗口右界到索引2时,窗口包含 1,3,5,满足条件 → 计数1。
    • 右界到索引3时,仍满足条件 → 计数+1。

总结

  • 推荐解法一:单次遍历法在时间和空间上均为最优,适合处理大规模数据。
  • 滑动窗口法:适合对双指针熟悉的开发者,逻辑相对直观。
  • 暴力优化法:仅在小规模数据或验证场景下使用。

所有代码均已在力扣提交通过,可直接复制使用!


欢迎在评论区交流其他思路或优化方法!

相关文章:

  • 121. 买卖股票的最佳时机
  • 第八章 IO流
  • 深圳举办2025年全国儿童预防接种日主题宣传活动 全生命周期健康守护再升级
  • Compose笔记(十九)--NestedScroll
  • 基于javaweb的SSM投票管理系统设计与实现(源码+文档+部署讲解)
  • 优化 Nginx 配置主域名重定向与 Mongrel 规则迁移
  • 网络攻防第一~四集
  • asammdf 库的高级功能:优化数据处理和分析
  • Android学习总结之协程对比优缺点(协程一)
  • TP4056 电池管理芯片介绍及电路应用
  • 1.1.1 用于排序规则的IComparable接口使用介绍
  • 直线模组精度测试的标准是什么?
  • 前端面试之吊打面试官 HTML篇
  • 关闭网桥的STP,解决RHEL10上qemu使用u-boot加载uImage自动加载失败的问题
  • RNN——循环神经网络
  • 基于YOLO与PySide6的道路缺陷检测系统(源码)
  • 数据库MySQL学习——day5(总结与复习实践)
  • AAAI2016论文 UCO: A Unified Cybersecurity Ontology
  • i18n-ai-translate开源程序,可以使用DeepSeek等模型将您的 i18nJSON翻译成任何语言
  • PyTorch作为深度学习框架在建筑行业的应用
  • 我国首个大型通用光谱望远镜JUST在青海启动建设
  • 戴昕谈隐私、数据、声誉与法律现实主义
  • 男子称喝中药治肺结节三个月后反变大增多,自贡卫健委回应
  • 打破“内卷”与“焦虑”怪圈,在阅读中寻找松弛感
  • 推进“即买即退”服务试点,上海静安离境退税商店近400家居全市首位
  • 中华人民共和国和肯尼亚共和国关于打造新时代全天候中非命运共同体典范的联合声明