hot100:10和为k的子数组

题目链接:

LeetCode 热题 100 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台

算法思想:

法一(暴力法):

使用两层for循环列举出所有的子数组,使用一个变量sum求得子数组的和并判断是否为k

//暴力解法
    public int subarraySum(int[] nums, int k) {
        int ret = 0;
        for(int i = 0;i < nums.length;i++) {
            int sum = 0;
            //因为某单个元素也是子数组,所以内层循环j=i
            for(int j = i;j < nums.length;j++) {
                sum += nums[j];
                if(sum == k) {
                    ret++;
                }
            }
        }
        return ret;
    }
法二(前缀和+哈希表):

前缀和:前缀和就是从下标为0的元素开始到当前下标之间所有元素的和(假设是一个数组arr的话,下标为i对应的前缀和就等于arr[0]+arr[1]+arr[2]+......+arr[i-1]+arr[i]),举一个列子:

假设给定一个数组为arr:{1,3,5,7,9}

前缀和数组就是sum:{1,4,9,16,25}

所以就可以推导出前缀和的公式:sum[i]=sum[i-1]+arr[i],如果计算给定下标范围(i,j)之间元素的和可以使用arr[i]+arr[i+1]+......+arr[j-1]+arr[j]=sum[j]-sum[i-1]

为了加深大家对前缀和的理解,这样附上一道有关前缀和的算法题: 【模板】前缀和_牛客题霸_牛客网

 代码如下:

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int q = scanner.nextInt();
        int[] array = new int[n+1];
        for(int i = 1;i <= n;i++) {
            array[i] = scanner.nextInt();
        }

        long[] dp = new long[n+1];
        for(int i = 1;i <= n;i++) {
            dp[i] = dp[i-1] + array[i];
        }

        while(q > 0) {
            int l = scanner.nextInt();
            int r = scanner.nextInt();
            System.out.println(dp[r] - dp[l-1]);
            q--;
        }
    }
}

有了前缀和的知识来解决整个题目就会变得相对简单一点了,假设某个下标范围内(i,j)之间的和为k,那么根据前缀和可知sum[j]=sum[i-1]+k,经过简单移项之后可以得到sum[i-1]=sum[j]-k,那么整个问题就转换成了以j下标为基准,之前有几个子数组的前缀和为sum[j]-k,k是已知的,sum[j]可以使用相加的方式算出,通过遍历数组,计算每个位置的前缀和,并使用一个哈希表来存储每个前缀和出现的次数,在遍历的过程中,我们检查是否存在sum[j] - k的前缀和,如果存在,说明从某个位置到当前位置的连续子数组的和为k,我们将对应的次数累加到结果中

    //法二:前缀和+哈希表
    public int subarraySum(int[] nums, int k) {
        Map<Integer,Integer> map = new HashMap<>();
        map.put(0,1);

        int sum = 0;
        int ret = 0;
        for(int x : nums) {
            sum += x;//计算前缀和
            ret += map.getOrDefault(sum-k,0);//统计结果
            map.put(sum,map.getOrDefault(sum,0)+1);//更新map中的前缀和
        }
        return ret;
    }

 注意:这里需要预先在map中存入一次为0的前缀和,目的是为了防止下标从0开始累加到某一位时前缀和刚好时k,这样刚好存在一个子数组和为k,那么就会在map寻找sum-k=0的前缀和出现了几次,如果不提前预存一个的话就会漏掉一种结果。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值