首页 > 其他分享 >LeetCode题练习与总结:区间和的个数--327

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

时间:2024-10-21 22:21:26浏览次数:9  
标签:lower 递归 nums -- prefixSum int 327 数组 LeetCode

一、题目描述

给你一个整数数组 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 语句来处理不同的情况,例如当左半部分的指针超过边界时,只需要处理右半部分。

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

标签:lower,递归,nums,--,prefixSum,int,327,数组,LeetCode
From: https://blog.csdn.net/weixin_62860386/article/details/143064328

相关文章

  • 2024/10/21 模拟赛总结
    \(100+50+0+5=155\),T3三目没打括号太爽了#A.串串串基本上就是前缀异或和板子交换两个\(0,1\)不会改变奇偶性,所以可以直接疑惑判断//BLuemoon_#include<bits/stdc++.h>usingnamespacestd;constintkMaxN=2e5+5;intn,m,q,l1,r1,l2,r2,f[kMaxN],g[k......
  • LeetCode题练习与总结:奇偶链表--328
    一、题目描述给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。你必须在 ......
  • 图论模板
    最短路(dijkstra)无法处理负边权,时间复杂度O(mlogn)#include<bits/stdc++.h>#definefo(i,a,b)for(ll(i)=(a);(i)<=(b);(i)++)#definefd(i,b,a)for(ll(i)=(b);(i)>=(a);(i)--)#definelc(o<<1)#definerc((o<<1)|1)#definemk(x,y)make_pair((x),(......
  • Java中super关键词的用法和注意事项
    在Java中,super关键字用于引用当前对象的父类。它主要有以下几种用途:1.访问父类的属性和方法:当子类中定义了与父类同名的属性或方法时,可以使用super关键字来明确指出要访问的是父类中的属性或方法。2.调用父类的构造器:在子类的构造方法中,可以使用super()来显式调用父类的构造器,以......
  • 基于单片机GPS跌倒和心电老人防护监测仪系统
    **文章目录前言概要设计思路软件设计效果图程序文章目录前言......
  • 2024-10-21每日一题
    P1223排队接水题目描述有\(n\)个人在一个水龙头前排队接水,假如每个人接水的时间为\(T_i\),请编程找出这\(n\)个人排队的一种顺序,使得\(n\)个人的平均等待时间最小。输入格式第一行为一个整数\(n\)。第二行\(n\)个整数,第\(i\)个整数\(T_i\)表示第\(i\)个人的......
  • 基于单片机的人体感应智能台灯系统
    **文章目录前言概要功能设计设计思路软件设计效果图程序文章目录前言......
  • FCITX5的一些小命令
    请注意,这是我日常使用的小笔记,不一定能百分百解决问题,仅做学习参考。使用kde桌面环境,但提示fcitx的KCModule未找到,它的软件包名字有可能是kcm-fcitx5,kde-config-fcitx5(debian)或fcitx5-configtool(dnf)。这三种在ManjaroLinux上安装fcitx5时遇到依赖关系问题,可以尝试以......
  • MySQL—CRUD—进阶—(二) (ಥ_ಥ)
    文本目录:❄️一、新增: ❄️二、查询:       1、聚合查询:             1)、聚合函数:             2)、GROUPBY子句:             3)、HAVING子句:      2、联合查询:  ......
  • 为什么有时候用 translate 来改变位置, 而不是定位方式? (如top, left)
    在前端开发中,我们有时候会选择使用translate来改变元素的位置,而不是使用传统的定位方式(如top,left,right,bottom),主要是因为性能方面的考虑。具体来说,translate是通过CSS中的transform属性实现的,它操作的是元素的渲染层,而不是布局层。ps:这里的渲染......