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

LeetCode题练习与总结:区间和的个数--327

一、题目描述

给你一个整数数组 nums 以及两个整数 lower 和 upper 。求数组中,值位于范围 [lower, upper] (包含 lower 和 upper)之内的 区间和的个数 。

区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。

示例 1:

输入:nums = [-2,5,-1], lower = -2, upper = 2
输出:3
解释:存在三个区间:[0,0]、[2,2] 和 [0,2] ,对应的区间和分别是:-2 、-1 、2 。

示例 2:

输入:nums = [0], lower = 0, upper = 0
输出:1

提示:

  • 1 <= nums.length <= 10^5
  • -2^31 <= nums[i] <= 2^31 - 1
  • -10^5 <= lower <= upper <= 10^5
  • 题目数据保证答案是一个 32 位 的整数

二、解题思路

  1. 首先计算前缀和数组,前缀和数组的第 i 个元素表示 nums 数组从下标 0 到下标 i-1 的元素之和。这样,区间和 S(i, j) 可以通过前缀和数组快速计算得到,即 S(i, j) = prefixSum[j+1] - prefixSum[i]。

  2. 使用归并排序的思想来解决这个问题。在归并排序的过程中,统计满足条件的区间和的个数。

  3. 在归并排序的合并过程中,对于左半部分的每一个前缀和,找到右半部分中满足 lower <= prefixSum[j] - prefixSum[i] <= upper 的前缀和的个数。

  4. 最终统计满足条件的区间和的个数。

三、具体代码

class Solution {public int countRangeSum(int[] nums, int lower, int upper) {long[] prefixSum = new long[nums.length + 1];for (int i = 0; i < nums.length; i++) {prefixSum[i + 1] = prefixSum[i] + nums[i];}return mergeSort(prefixSum, 0, nums.length, lower, upper);}private int mergeSort(long[] prefixSum, int left, int right, int lower, int upper) {if (left >= right) {return 0;}int mid = left + (right - left) / 2;int count = mergeSort(prefixSum, left, mid, lower, upper) + mergeSort(prefixSum, mid + 1, right, lower, upper);int j = mid + 1, k = mid + 1;for (int i = left; i <= mid; i++) {while (j <= right && prefixSum[j] - prefixSum[i] < lower) j++;while (k <= right && prefixSum[k] - prefixSum[i] <= upper) k++;count += k - j;}long[] sorted = new long[right - left + 1];int p1 = left, p2 = mid + 1, p = 0;while (p1 <= mid || p2 <= right) {if (p1 > mid) {sorted[p++] = prefixSum[p2++];} else if (p2 > right) {sorted[p++] = prefixSum[p1++];} else {if (prefixSum[p1] <= prefixSum[p2]) {sorted[p++] = prefixSum[p1++];} else {sorted[p++] = prefixSum[p2++];}}}System.arraycopy(sorted, 0, prefixSum, left, sorted.length);return count;}
}

这段代码首先计算了前缀和数组,然后通过递归的方式使用归并排序来统计满足条件的区间和的个数。在合并过程中,通过双指针技巧找到满足条件的区间和。最终返回统计的结果。

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 计算前缀和数组 prefixSum 的时间复杂度是 O(n),其中 n 是数组 nums 的长度。

  • mergeSort 方法是一个递归方法,它会将数组 prefixSum 分成两半,并对每一半递归地进行归并排序。

  • 在每一层的递归中,我们需要合并两个已排序的子数组。合并过程中,对于左半部分的每一个前缀和,我们使用两个指针 j 和 k 来找到满足条件的区间和的个数,这个过程的时间复杂度是 O(n)。

  • 因此,在每一层的递归中,我们需要 O(n) 的时间来合并数组,并且有 log(n) 层递归(因为每次递归都是将数组长度减半)。

综上所述,总的时间复杂度是 O(n log n)。

2. 空间复杂度
  • 前缀和数组 prefixSum 占用 O(n) 的空间。

  • 递归方法 mergeSort 的空间复杂度主要取决于递归的深度和临时数组 sorted。递归的深度是 O(log n),而临时数组 sorted 在每一层递归中都是大小为 O(n)。

因此,总的空间复杂度是 O(n) + O(log n) * O(n) = O(n)。

五、总结知识点

  • 前缀和数组

    • 用于快速计算任意子数组的和。
    • 前缀和数组的第 i 个元素表示原数组从第 0 个元素到第 i-1 个元素的和。
  • 归并排序

    • 利用分治策略将数组分成更小的部分进行排序,然后合并这些有序部分。
    • 归并排序的时间复杂度是 O(n log n),是一种稳定的排序算法。
  • 递归

    • 递归是函数调用自身的一种方法。
    • 在归并排序中,递归用于将问题分解为更小的子问题。
  • 双指针技术

    • 在合并过程中,使用两个指针 j 和 k 来统计满足特定条件的区间和的数量。
    • 通过移动指针,可以在 O(n) 时间内完成统计。
  • 数组拷贝

    • 使用 System.arraycopy 方法来高效地复制数组中的元素。
    • 这是 Java 标准库提供的一种优化过的数组复制方法。
  • 边界检查

    • 在合并排序和统计区间和的过程中,代码中包含了对数组边界的检查,以避免数组越界。
  • 逻辑判断

    • 使用 if-else 语句来处理不同的情况,例如当左半部分的指针超过边界时,只需要处理右半部分。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。


http://www.mrgr.cn/news/55131.html

相关文章:

  • C++源码生成·序章
  • <项目代码>YOLOv8路面病害识别<目标检测>
  • qt编译报错大量error C2065: 未声明的标识符
  • STM32嵌入式移植GmSSL库
  • 干货|react router- loader 和组件 useEffect 加载数据的选择
  • 深入解析TCP/IP协议:网络通信的基石
  • 面向对象与设计模式第一课:深入理解OOP
  • 机器学习——量子机器学习(Quantum Machine Learning)
  • Js中,const为什么常用来定义函数?
  • 基于大模型的招聘智能体:从创意到MVP
  • 【RoadRunner】自动驾驶模拟3D场景构建 | 自定义交叉口工具详解
  • Android SELinux——添加策略实例(十五)
  • 组装电脑主板配置全解析:从入门到精通
  • SSDF攻击及防御PPT及讲稿
  • 【漏洞复现】华望云 会议管理平台 deptactionlist 后台SQL注入漏洞
  • 基于单片机的多功能鱼缸控制系统设计
  • Java数组的特性与实现、与其他语言的区别、多维数组的遍历、底层实现及其内存管理
  • YoloV9改进策略:归一化改进| ContraNormYoloV8中的创新应用(全网首发)
  • Java反射深入学习
  • c语言字符串函数strstr,strtok,strerror
  • AIGC实战——世界模型(World Model)
  • MiniConda 的安装与使用
  • 【学术会议投稿】Java Web开发实战:从零到一构建动态网站
  • 【学术会议投稿】机器学习框架终极指南:PyTorch vs TensorFlow vs Keras vs Scikit-learn
  • Anomalib 1.x 系列之二:自定义数据
  • 手动部署Java项目、nginx前端和mysql数据库到centos虚拟机