20天速通LeetCode day07:前缀和

前言

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

560:和为k的子数组

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

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

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

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

  1. 定义sumi表示前i个元素和
  2. 一个子数组j+1,i的和为k,等价于sumi−sumj=k⇒sumj=sumi−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

要求:answeri=nums 中除 numsi 之外所有元素的乘积

核心思路

  1. 前缀积:prefixi表示nums0...i-1的乘积
  2. 后缀积:suffixi表示numsi+1...n-1的乘积
  3. 那么answeri=prefixi*suffixi

代码实现

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 代替后缀积数组

相关推荐
毕竟是shy哥1 小时前
计算YOLO数据集中每个类的目标数
算法·yolo·机器学习
M78佐菲1 小时前
Linux学习笔记:TCP协议
linux·笔记·学习·tcp/ip·算法
圣保罗的大教堂1 小时前
leetcode 1406. 石子游戏 III 困难
leetcode
晊晌_h2 小时前
嵌入式从0到精通——数据结构总结[特殊字符]
数据结构·算法·排序算法
我找到地球的支点啦3 小时前
Matlab系列(009) 一CRC循环冗余校验详解
开发语言·数据结构·算法·matlab·信息与通信
罗西的思考3 小时前
【OpenClaw具身硬件】ZeroClaw 源码阅读笔记(3)--- RAG
人工智能·算法·机器学习
浪里镖客4 小时前
位姿转换矩阵写法-个人习惯(计算机理解其实是相反的)
线性代数·算法·矩阵
小白羊丨7 小时前
如何诊断 Prompt 模板导致的效果下降?
人工智能·算法·prompt
OPEN-F8 小时前
C++11/14新特性精讲:移动语义与智能指针实战
开发语言·c++·算法
lisin-lee-cooper8 小时前
【leetcode658】有序数组找出k个最接近x的数
java·数据结构·算法