• LeetCode 560. 和为K的子数组


    我的LeetCode:https://leetcode-cn.com/u/ituring/

    我的LeetCode刷题源码[GitHub]:https://github.com/izhoujie/Algorithmcii

    LeetCode 560. 和为K的子数组

    题目

    给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

    示例 1 :

    输入:nums = [1,1,1], k = 2
    输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
    

    说明 :

    • 数组的长度为 [1, 20,000]。
    • 数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/subarray-sum-equals-k
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    解题思路

    本题与LeetCode 1248. 统计「优美子数组」为同类问题,可对比学习;

    思路1-用map记录累加和

    思路解析:如果i和j之间的和为k,i之前的和为sum1,j之前的和为sum2,那么就有sum2-sum1=k,所以使用map一次遍历并记录以sum为key,value为其出现的次数;
    需要注意的是,起始map需要添加(0,1)对,代表sum-k为0时出现了1次,举个例子,若k=10,数组第一项就是10,那么sum-k=0,但0此时不在map,就少了一次count;

    1. 建map,初始add(0,1),新建统计变量count=0;
    2. 遍历累加sum,且看map中是否有sum-k,有则累加至count;
    3. 以sum为key,更新map;

    算法复杂度:

    • 时间复杂度: $ {color{Magenta}{Omicronleft(n ight)}} $
    • 空间复杂度: $ {color{Magenta}{Omicronleft(n ight)}} $ map 最多需要保存n+1个key-value对

    思路2-改用数组替代map记录中间值

    与思路1唯一的区别是保存中间值数据结构的变更;
    首先需要知道所有连续和的最大值与最小值,创建用以记录连续和的数组;
    步骤:

    1. 遍历求和sum并记录和的最大值max与最小值min,用以确定要创建数组map的长度max-min+1;
    2. 再次遍历求和sum,若和为k则count++,然后判断sum-k的范围,若在[min,max]范围内说明存在连续和为k的子数组,统计map[sum-key-min]到count上,并记录map[sum-min]++;
      • 首先sum-min肯定存在的,但是现在要多求k个值,于是需要在sum-min中多减去k,但是需要先判断sum-k之后的值还在不在[min,max]内,若在肯定存在解,此时再减去min即可;

    算法复杂度:

    • 时间复杂度: $ {color{Magenta}{Omicronleft(n ight)}} $
    • 空间复杂度: $ {color{Magenta}{Omicronleft(m ight)}} $ m为max与min的差值

    算法源码示例

    package leetcode;
    
    import java.util.HashMap;
    
    /**
     * @author ZhouJie
     * @date 2020年4月21日 下午8:47:12 
     * @Description: 560. 和为K的子数组
     *
     */
    public class LeetCode_0560 {
    
    }
    
    class Solution_0560 {
    	/**
    	 * @author: ZhouJie
    	 * @date: 2020年4月21日 下午8:47:49 
    	 * @param: @param nums
    	 * @param: @param k
    	 * @param: @return
    	 * @return: int
    	 * @Description: 1-map存储前缀和;
    	 *
    	 */
    	public int subarraySum_(int[] nums, int k) {
    		HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
    		// 初始必须存入(0,1),若不存而数组的第一项就是k,sum-k=0时就找不到0了
    		map.put(0, 1);
    		int sum = 0, count = 0;
    		for (int val : nums) {
    			sum += val;
    			int key = sum - k;
    			// 寻找之前是不是存过sum-k,有就表示找到了一个和为k的片段
    			if (map.containsKey(key)) {
    				count += map.get(key);
    			}
    			// 更新和为sum的出现次数
    			map.put(sum, map.getOrDefault(sum, 0) + 1);
    		}
    		return count;
    	}
    
    	/**
    	 * @author: ZhouJie
    	 * @date: 2020年5月15日 下午10:22:40 
    	 * @param: @param nums
    	 * @param: @param k
    	 * @param: @return
    	 * @return: int
    	 * @Description: 2-使用辅助数组替代map保存中间值;
    	 *
    	 */
    	public int subarraySum_2(int[] nums, int k) {
    		int min = 0, max = 0, sum = 0, count = 0, key;
    		// 求最大值和最小值,确定所有连续和的范围
    		for (int val : nums) {
    			sum += val;
    			max = Math.max(max, sum);
    			min = Math.min(min, sum);
    		}
    		// 用以记录连续和与min差的数组
    		int[] map = new int[max - min + 1];
    		sum = 0;
    		for (int val : nums) {
    			sum += val;
    			// 若连续和等于k直接记录
    			if (sum == k) {
    				count++;
    			}
    			// 在map中寻找其他连续和为sum-k-min的数量
    			// 解析:首先sum-min肯定存在,但是现在要多个k值,于是尝试多减去k,看剩余值仍否在[min,max]范围内,若在说明存在这样的连续子数组
    			key = sum - k;
    			if (key >= min && key <= max) {
    				count += map[key - min];
    			}
    			// 记录sum-min的数量
    			map[sum - min]++;
    		}
    		return count;
    	}
    }
    
  • 相关阅读:
    《Java技术》第七次作业
    《Java技术》第六次作业
    《Java技术》第五次作业
    《Java技术》第四次作业
    《Java技术》第三次作业
    《Java技术》第二次作业
    《Java技术》第一次作业
    股票——布林带
    股票——指数移动平均线
    股票——简单移动平均线
  • 原文地址:https://www.cnblogs.com/izhoujie/p/12747826.html
Copyright © 2020-2023  润新知