本题来自左神《程序员代码面试指南》“未排序正数数组中累加和为给定值的最长子数组长度”题目。

题目

牛客OJ:未排序数组中累加和为给定值的最长子数组长度

题解

本文提供的方法可以做到时间复杂度为O(N)、额外空间复杂度为O(N)。

为了说明解法,先定义s 的概念,s(i)代表子数组arr[0…i]所有元素的累加和。那么子数组arr[j…i(] 0≤j≤i<arr.length)的累加和为s(i)-s(j-1),因为根据定义,s(i)=arr[0…i]的累加和等于arr[0…j-1]的累加和与arr[j…i]的累加和相加,又有arr[0…j-1]的累加和为s(j-1)。所以,arr[j…i]的累加和为s(i)-s(j-1),这个结论是求解这道题的核心。

整个过程只遍历一次arr,具体过程为:

  1. 设置变量sum=0,表示从0 位置开始一直加到i 位置所有元素的和。设置变量len=0,表示累加和为k 的最长子数组长度。设置哈希表map,其中,key 表示从arr 最左边开始累加的过程中出现过的sum 值,对应的value 值则表示sum 值最早出现的位置。
  2. 从左到右开始遍历,遍历的当前元素为arr[i]。
    1. 令sum=sum+arr[i],即之前所有元素的累加和s(i),在map 中查看是否存在sum-k。

      • 如果 sum-k 存在,从map 中取出sum-k 对应的value 值,记为j,j 代表从左到右不断累加的过程中第一次加出sum-k 这个累加和的位置。根据之前得出的结论,arr[j+1…i]的累加和为s(i)-s(j),此时s(i)=sum,又有s(j)=sum-k,所以arr[j+1…i]的累加和为k。同时因为map 中只记录每一个累加和最早出现的位置,所以此时的arr[j+1…i]是在必须以arr[i]结尾的所有子数组中,最长的累加和为k 的子数组,如果该子数组的长度大于len,就更新len。
      • 如果 sum-k 不存在,说明在必须以arr[i]结尾的情况下没有累加和为k 的子数组。
    2. 检查当前的sum(即s(i))是否在map 中。如果不存在,说明此时的sum 值是第一次出现的,就把记录(sum,i)加入到map 中。如果sum 存在,说明之前已经出现过sum,map 只记录一个累加和最早出现的位置,所以此时什么记录也不加。
  3. 继续遍历下一个元素,直到所有的元素遍历完。

大体过程如上,但还有一个很重要的问题需要处理。根据arr[j+1…i]的累加和为s(i)-(j),所以,如果从0 位置开始累加,会导致j+1≥1。也就是说,所有从0 位置开始的子数组都没有考虑过。所以,应该从-1 位置开始累加,也就是在遍历之前先把(0,-1)这个记录放进map,这个记录的意义是如果任何一个数都不加时,累加和为0。这样,从0 位置开始的子数组就被我们考虑到了。

具体过程请参看如下代码中的 maxLength 方法。

package chapter_8_arrayandmatrix;import java.util.HashMap;public class Problem_11_LongestSumSubArrayLength {public static int maxLength(int[] arr, int k) {if (arr == null || arr.length == 0) {return 0;}HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); // 含义:<从0位置元素的累加和, 满足此累加和的最小下标>map.put(0, -1); // 注意不要写成 (0, 0)int len = 0;int sum = 0;for (int i = 0; i < arr.length; i++) {sum += arr[i];if (map.containsKey(sum - k)) {len = Math.max(i - map.get(sum - k), len);}if (!map.containsKey(sum)) {map.put(sum, i);}}return len;}public static int[] generateArray(int size) {int[] result = new int[size];for (int i = 0; i != size; i++) {result[i] = (int) (Math.random() * 11) - 5;}return result;}public static void printArray(int[] arr) {for (int i = 0; i != arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}public static void main(String[] args) {int[] arr = generateArray(20);printArray(arr);System.out.println(maxLength(arr, 10));}
}

附:ACM赛制牛客题解(需手动输入输出),即我的解法,已通过全部测试用例

package test;import java.util.*;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int k = scanner.nextInt();int[] arr = new int[n];for (int i = 0; i < n; i++) {arr[i] = scanner.nextInt();}// solutionMap<Integer, Integer> map = new HashMap<>(); // <从0位置元素的累加和, 满足此累加和的最小下标>map.put(0, -1); // 注意不要写成 (0, 0)int maxLen = 0, curSum = 0;for (int i = 0; i < n; i++) {curSum += arr[i];if (!map.containsKey(curSum)) {map.put(curSum, i);}int gap = curSum - k;if (map.containsKey(gap)) {maxLen = Math.max(i - map.get(gap), maxLen);}}System.out.println(maxLen);}
}
/*
【示例1】
11 0
1 -2 1 1 1 -1 1 -1 1 -1 2
答案:9
解析:1 [-2 1 1 1 -1 1 -1 1 -1] 2【示例2】
20 0
-1 1 0 4 5 -2 2 -2 2 -2 2 -4 4 5 -2 -2 -1 1 1 1
答案:12
解析:-1 1 0 4 5 [-2 2 -2 2 -2 2 -4 4 5 -2 -2 -1] 1 1 1*/

左神算法:未排序正数数组中累加和为给定值的最长子数组长度(Java版)相关推荐

  1. 未排序正整数组中累加和为指定值的最长子数组长度

    题目:给定一个数组arr,该数组无序,但每个值都为正数,在给定一个正数k.求arr中所有的子数组中所有元素累加为k的最长子数组长度.  例如,arr = [1,2,1,1,1],k = 3.  累加和 ...

  2. 牛客题霸 [ 未排序数组中累加和为给定值的最长子数组长度] C++题解/答案

    牛客题霸 [ 未排序数组中累加和为给定值的最长子数组长度] C++题解/答案 题目描述 给定一个无序数组arr, 其中元素可正.可负.可0.给定一个整数k,求arr所有子数组中累加和为k的最长子数组长 ...

  3. 未排序数组中累加和为给定值的最长子数组系列问题

    题目:给定一个无序数组arr,其中元素可正.可负.可 0,给定一个整数k.求arr所有子数组中累加和为k的最长子数组长度. 补充问题: 1.给定一个无序数组arr,其中元素可正.可负.可 0.求arr ...

  4. 左神算法:判断 t1 树中是否有与 t2 树拓扑结构完全相同的子树(Java版)

    本题来自左神<程序员代码面试指南>"判断 t1 树中是否有与 t2 树拓扑结构完全相同的子树"题目. 题目 给定彼此独立的两棵树头节点分别为 t1 和 t2,判断 t1 ...

  5. 左神算法:在二叉树中找到累加和为指定值的最长路径长度(Java版)

    本题来自左神<程序员代码面试指南>"在二叉树中找到累加和为指定值的最长路径长度"题目. 题目 给定一棵二叉树的头节点 head 和一个 32 位整数 sum,二叉树节点 ...

  6. 左神算法:调整搜索二叉树中两个错误的节点(Java版)

    本题来自左神<程序员代码面试指南>"调整搜索二叉树中两个错误的节点"题目. 题目 原问题: 一棵二叉树原本是搜索二叉树,但是其中有两个节点调换了位置,使得这棵二叉树不再 ...

  7. 算法013:二维数组中的查找-在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断中是否存在

    题目:在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数.示例: 现有 ...

  8. 左神算法:最大值减去最小值小于或等于num的子数组的数量(Java版)

    本题来自左神<程序员面试代码指南>"最大值减去最小值小于或等于num的子数组的数量"题目. 题目 给定数组 arr 和整数 num,共返回有多少个子数组满足如下情况: ...

  9. 左神算法:求最大子矩阵的大小(Java版)

    本题来自左神<程序员面试代码指南>"求最大子矩阵的大小"题目. 题目 给定一个整型矩阵 map,其中的值只有0和1两种,求其中全是1的所有矩形区域中,最大的矩形区域为1 ...

最新文章

  1. 九大网络安全失误,需要注意
  2. pandas使用dropna函数删除dataframe中列非缺失值的个数小于某一比例阈值的数据列
  3. 从windows上传文件到linux,中文名乱码解决方法
  4. Android GIF 编解码
  5. 美国数学三大分支专业就业前景解析 你选对了吗?
  6. VS2015 提示 无法启动 IIS Express Web 服务器
  7. boost::::adaptors::indexed::indexed相关的测试程序
  8. 数学建模亚太赛优秀论文_2019亚太地区大学生数学建模竞赛志愿者等级评定结果公布!...
  9. xp系统debugger用户_xp系统BIOS恢复出厂设置开机需按F1的具体步骤--win10专业版
  10. 企业信息化解决方案——插件式平台开发框架
  11. 目标跟踪学习之MDNet
  12. k8s相关面试问题_最常被问到的20道Kubernetes面试题
  13. 那些在错误道路上一路狂奔的国产VR
  14. 今天开始清理个人计算机资料了
  15. 决策树 Decision Tree
  16. 什么是分布式定时任务框架?
  17. Kubernetes之YAML语法
  18. Freertos消息队列接收源码xQueueGenericReceive分析
  19. web前端—前端三剑客之JS(12):字符串
  20. 听音室-HIFI入门之400多张发烧碟中选出的精品

热门文章

  1. java开发和python哪个好学_学编程,Python和Java哪个更好?
  2. UVA - 11572
  3. Windows Sockets 2.0 新特性
  4. 秋天的第一份“干货” I Referer 防盗链,为什么少了个字母 R?
  5. ElasticSearch探索之路(五)集群与分片:选举、动态更新、近实时搜索、事务日志、段合并
  6. 面试官:InnoDB中一棵B+树可以存放多少行数据?
  7. 并发环境下,先操作数据库还是先操作缓存?
  8. 再看Kafka Lag
  9. MySQL基础总结(一)
  10. Union-Find 算法应用