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

相关推荐
倒头就睡的小比特1 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!1 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼1 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
m0_547486661 天前
《数据结构教程》全套 PPT课件2026
数据结构
旖旎夜光1 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark1 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her11 天前
并查集-听课笔记
笔记·算法·并查集
码流子1 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven1 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark1 天前
从算法设计模式看编程思维的抽象能力4
算法