未排序数组中累加和为给定值的最长子数组长度
问题描述:给定一个无序数组arr, 其中元素可正、可负、可0。给定一个整数k,求arr所有子数组中累加和为k的最长子数组长度
示例
输入:[1,-2,1,1,1],0
返回值:3
说明:最长子数组为[1,-2,1],其长度为3
方法一
思路分析
本题我们能直接想到的办法就是暴力穷举,外层循环从数组开始位置遍历,内层循环遍历从上一层的位置开始,使用sum记录当前数组的和,如果达到k,将数组长度记录下来,之后继续向后遍历,当内层循环遍历到数组结束位置后,外层循环往下继续刚才的操作。这种方法时间复杂度较大。因此我们考虑使用前缀和数组,定义数组$pre[i]=arr[0]+arr[1]+...+arr[i-1]$,那么子数组$arr[i]$到$arr[j-1]$的和为$pre[j]-pre[i]$而后计算出所有可能的前缀和数组,特别的数组中第一个元素的前缀和初始化为0,接着我们根据子数组的可能的长度,按长度的最大值开始向下遍历,即按照数组长度由大到小找到原数组中可能的子数组,如果子数组的和$pre[j]-pre[i]$为k,则输出当前的子数组长度。
图解
输入:[1,-2,1,1,1],0
首先计算其前缀数组$pre[6]=\{0,1,-1,0,1,2\}$
$len$ | 子数组的起止位置$i$ | 子数组的和 |
5 | 0 | $presum[0+5]-presum[0]=2$ |
4 | 0 | $presum[0+4]-presum[0]=1$ |
1 | $presum[1+4]-presum[1]=1$ | |
3 | 0 | $presum[0+3]-presum[0]=0$ $k=0$程序在此处运行结束 |
1 | | |
2 | |
JAVA核心代码
import java.util.*; public class Solution { /** * max length of the subarray sum = k * @param arr int整型一维数组 the array * @param k int整型 target * @return int整型 */ public int maxlenEqualK (int[] arr, int k) { int n=arr.length; int[] presum=new int[n+1]; for(int i=0;i<n;i++){ presum[i+1]=presum[i]+arr[i];////构建前缀和数组,开始元素的前缀和为0,因此设置n+1个长度 } //逆序遍历所有可能的长度 for(int len=n;len>=1;len--) { for(int i=0;i+len<=n;i++) { if(presum[i+len]-presum[i]==k)//长度为len的子数组的和 { return len; } } } return 0; } }
复杂度分析
- 时间复杂度:两层的循环,外层循环遍历$n$,内存循环遍历$n-len$,因此时间复杂度为$O(n^2)$
- 空间复杂度: 借助于一个前缀数组,因此空间复杂度为$O(n)$
方法二
思路分析
本题也可采用哈希表的办法,将当前数组arr[0,i]的和做为关键字,i做为值记录在哈希表hashmap中,循环遍历数组,从开始位置出发,记录前缀数组的和presum,如果这个值不在哈希表中,则将其记录在哈希表中,继续判断presum-k是否在哈希表中,如果也在哈希表中,那么i-hashmap[presum-k]这一数值就可作为累加和为k的数组长度,但不一定是最长的,需要继续进行遍历,直到遍历完成。特别的由于数组中可能存在0,而子数组增加0,数组的和不变,长度会增加,因此初始hashmap[0]=-1,以避免以0开始的子数组的计算。
图解
输入:$[1,-2,1,1,1]$,$0$
初始化$hashmap[0]=-1$,$length$初始化为0
$i$ | $presum +=arr[i]$ | $hashmap$ | $length$ |
$0$ | $1$ | $hashmap[1]=0$ | $0$ |
$1$ | $1+(-2)=-1$ | $hashmap[-1]=1$ | $0$ |
$2$ | $-1+1=0$ | ---------- | $max(length,i-hashmap[presum-k])=max(0,3)=3$ |
$3$ | $0+1=1$ | ---------- | $max(length,i-hashmap[presum-k])=max(3,3)=3$ |
$4$ | $1+1=2$ | $hashmap[2]=4$ | $max(length,i-hashmap[presum-k])=max(3,0)=3$ |
python核心代码
# # max length of the subarray sum = k # @param arr int整型一维数组 the array # @param k int整型 target # @return int整型 # class Solution: def maxlenEqualK(self , arr , k ): # write code here if arr == None&nbs***bsp;len(arr) == 0: return 0 hashmap = {}#构建哈希表 hashmap[0] = -1#防止数组中有0的情况,数组中增加0,数组的和不变,数组长度+1 presum = 0 length = 0 for i in range(len(arr)): presum += arr[i]#构建前缀数组和 if presum not in hashmap:#哈希表中为存放当前值 hashmap[presum]=i if (presum - k) in hashmap:#presum-k的值在哈希表中,其长度也是最长的,因此可以直接计算总的长度 length = max(length,i-hashmap[presum-k]) return length
复杂度分析
- 时间复杂度:遍历一次数组,因此时间复杂度为$O(n)$
- 空间复杂度: 借助于一个presum变量以及一个哈希数组,因此空间复杂度为$O(n)$