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

相关推荐
.道阻且长.8 小时前
2.LeetCode算法习题讲解--双指针--复写零
算法·leetcode·职场和发展
To_OC10 小时前
LC 438 找到所有字母异位词:暴力超时后,我靠滑动窗口一招搞定
javascript·算法·leetcode
白狐_79810 小时前
408数据结构第5章:二叉树遍历序列题——技巧、判断与真题型总结
数据结构
Forever Nore12 小时前
学完C语言力扣第一题做不来正常吗
数据结构·算法
hansang_IR13 小时前
【题解】LC:倍增 / 区间并查集(Range Parallel Unionfind)
c++·算法·并查集
Tisfy14 小时前
LeetCode 3731.找出缺失的元素:哈希 / 排序
算法·leetcode·哈希算法·排序·哈希表
lucas_AI15 小时前
Q-CueGraph:你的多模态大模型会 zoom,但真的知道该看哪儿吗?
人工智能·算法
kaixin_啊啊15 小时前
test_机器学习算法学习
学习·算法·机器学习
liulilittle15 小时前
MOE路由:路由(logits: top-k/8)
c++·人工智能·算法·机器学习·llm