前言
有关哈希的基础知识,详见我上一篇“什么是哈希”的文章。
问题描述
给定一个数组arr,和一个整数num,求在arr中,累加和等于num的最长子数组的长度
例子:arr = {7,3,2,1,1,7,7,7} num = 7 其中有很多的子数组累加和等于7,但是最长的子数组是{3,2,1,1},所 以返回其长度4。
问题分析
第一种方法:暴力求解
这种方法很容易想到,但其时间复杂度为O(n²)。
为方便起见,假设数组arr={7,3,2,2,1},num=7.
从第一个数7开始,分别从前往后进行1个数、2个数、3个数.....的相加,加到最后一个数。然后从第二个数3开始,重复上述步骤,遍历所有情况,返回那个最长的长度。
遍历第一个数7
遍历第二个数3
此时发现有个加和等于7,即3+2+1+1=7,记录此时长度,为4,然后,再遍历下一个数2,再进行上述操作,直到所有情况遍历完毕。该算法的时间复杂度为O(n²),下面使用哈希表来是该问题的时间复杂度变为O(n)
第二种方法:使用哈希表
举个例子,arr={6,3,2,1,1},num=7。考虑这样一种情况,遍历该数组arr,每遍历到一个位置的时候,把该位置的数和前面的数先累加起来得到sum,然后减去num,看它前面的累加是否有出现过sum-num的值,如果出现,那么后面的这一块必定是等于num,长度即为当前遍历的位置减去前面累加和的最后一个位置。如图。
当遍历到arr[4]的时候,得到的sum=6+3+2+1=13,减去num,即减去7,得到6,看看前面是否有累加和为6的值。
一、发现有,则后面这一块的值必定为num,即为7,该子数组的长度为当前遍历位置减去前面累加和的最后一个位置,即6的位置。
二、若减去7得到一个结果并没有在前面的累加和出现过的话,则不更新最大长度。
三、把累加结果保存到哈希表
累加和可以使用哈希表来进行存储,查找和保存的结果都为O(1)的操作。可以知道,只需把数组遍历一遍即可得出答案,时间复杂度为O(N).
算法遍历到arr[4]时,哈希表如图。
代码
public static int maxLength(int[] arr, int k) {
if (arr == null || arr.length == 0) {
return 0;
}
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(0, -1); // important
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;
}
更多干货,请扫码关注“IT界的泥石流”微信公众号哦,带你领略编程之美!