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

相关推荐
谷雨不太卷6 小时前
进程的状态码
java·前端·算法
散峰而望6 小时前
【算法竞赛】C/C++ 的输入输出你真的玩会了吗?
c语言·开发语言·数据结构·c++·算法·github
躺不平的理查德6 小时前
时间复杂度与空间复杂度备忘录
数据结构·算法
yaki_ya6 小时前
yaki-C语言:从概念基础到内存解析---数组(array)完全指南
java·c语言·算法
刃神太酷啦6 小时前
扒透 STL 底层!map/set 如何封装红黑树?迭代器逻辑 + 键值限制全手撕----《Hello C++ Wrold!》(23)--(C/C++)
java·c语言·javascript·数据结构·c++·算法·leetcode
挽星安7 小时前
代码随想录算法训练营第五十天|卡码网 99 岛屿数量、卡码网 100 最大岛屿的面积
算法
葫三生7 小时前
《论三生原理》系列构建文理同构的认知体系?
人工智能·科技·深度学习·算法·机器学习·transformer
多加点辣也没关系7 小时前
数据结构与算法|第六章:队列
数据结构·算法·队列
_深海凉_8 小时前
LeetCode热题100-分割回文串
算法·leetcode·职场和发展