20天速通LeetCode day07:前缀和

前言

今日练习目的:掌握前缀和思维。前缀和的核心价值是能实现在O(1)时间求区间和,是各类子数组问题的常用工具

560:和为k的子数组

题目要求:给定一个整数数组nums和整数k

要求:统计并返回和为k的子数组

核心思路(前缀和+哈希表)

利用前缀和sum[i]表示数组前i个元素和,结合哈希表优化

  1. 定义sum[i]表示前i个元素和
  2. 一个子数组[j+1,i]的和为k,等价于sum[i]−sum[j]=k⇒sum[j]=sum[i]−k
  3. 用一个哈希表map记录前缀和出现的次数

代码实现

java 复制代码
class Solution {
    public int subarraySum(int[] nums, int k) {
        Map<Integer, Integer> prefixSum = new HashMap<>();
        prefixSum.put(0, 1);  // 初始化前缀和 0 出现一次
        int count = 0;
        int currSum = 0;

        for (int num : nums) {
            currSum += num; // 更新前缀和
            // 如果存在前缀和 currSum - k,则说明找到一个子数组
            count += prefixSum.getOrDefault(currSum - k, 0);
            // 更新哈希表
            prefixSum.put(currSum, prefixSum.getOrDefault(currSum, 0) + 1);
        }

        return count;
    }
}

总结

本题本质:通过前缀和+哈希表的方式。哈希表用来统计前缀和出现的次数

本题有点像两数之和,都是用哈希表快速查找差值 不同点在于

两数之和是找出下标

本题是统计出现的次数

209:长度最小的子数组

题目要求:给定一个正整数数组nums和一个整数target

要求:找出长度最小的连续子数组,使得子数组元素之和>=target。不存在则返回0

核心思路(前缀和+二分查找)

  1. 用前缀和快速求区间和
  2. 将问题转化为二分查找,找出 最小的右边界 j
  3. 使用Arrays.binarySearch 找 j

代码实现

java 复制代码
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
       int n=nums.length;
       int[] prefix=new int[n+1];
       //计算前缀和
       for(int i=0;i<n;i++){
        prefix[i+1]=prefix[i]+nums[i];
       }
       int minLen=Integer.MAX_VALUE;
       //遍历每个左边界
       for(int i=0;i<n;i++){
        int key=prefix[i]+target;
        //二分查找最小j,使prefix[j]>=key
        int j=Arrays.binarySearch(prefix,key);

        if(j<0){
            // binarySearch 返回 -(插入点)-1
            j=-j-1;
        }
        if(j<=n){
            minLen=Math.min(minLen,j-i);
        }
       }
       return minLen==Integer.MAX_VALUE?0:minLen;
    }
}

238:除了自身以外数组的乘积

题目要求:给定一个整数数组nums,返回一个数组answer

要求:answer[i]=nums 中除 nums[i] 之外所有元素的乘积

核心思路

  1. 前缀积:prefix[i]表示nums[0...i-1]的乘积
  2. 后缀积:suffix[i]表示nums[i+1...n-1]的乘积
  3. 那么answer[i]=prefix[i]*suffix[i]

代码实现

java 复制代码
public int[] productExceptSelf(int[] nums) {
    int n = nums.length;
    int[] answer = new int[n];

    // 前缀积
    answer[0] = 1;
    for (int i = 1; i < n; i++) {
        answer[i] = answer[i-1] * nums[i-1];
    }

    // 后缀积
    int R = 1;
    for (int i = n-1; i >= 0; i--) {
        answer[i] = answer[i] * R;
        R *= nums[i];
    }

    return answer;
}

总结

核心技巧:

用前缀积记录左边乘积

用后缀积记录右边乘积

相乘即可得到每个位置除自身外的乘积

优化空间:

先存前缀积在 answer

用一个变量 R 代替后缀积数组

相关推荐
小雅痞2 小时前
[Java][Leetcode hard] 42. 接雨水
java·开发语言·leetcode
载数而行5202 小时前
算法集训1:模拟,枚举,错误分析,前缀和,差分
算法
hehelm2 小时前
vector模拟实现
前端·javascript·算法
Tina学编程3 小时前
[HOT 100]今日一练------划分字母区间
算法·hot 100
RTC老炮3 小时前
RaptorQ前向纠错算法架构分析
网络·算法·架构·webrtc
故事和你913 小时前
洛谷-数据结构1-1-线性表2
开发语言·数据结构·算法·动态规划·图论
m0_555762903 小时前
从原始信号到IQ图的数学公式推导
算法
靠沿3 小时前
【递归、搜索与回溯算法】专题四——综合练习
算法·深度优先
qeen874 小时前
【数据结构】栈及其C语言模拟实现
c语言·数据结构·学习·