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

摘自:程序员代码面试指南

给定一个无序数组arr,其中元素可正、可负、可0。给定一个整数K,求arr所有的子数组中累加和为K的最长数组长度。

解答:

s(i)代表子数组arr[0…i]所有元素的累加和,那么子数组arr[j…i](0 <= j <= i < arr.length)的累加和为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中查看是否存在s(i) - 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是否在map中。如果不存在,说明此时的sum的值是第一次出现,就把记录(sum,i)加入到map中。如果sum存在,说明之前已经出现过sum,map只记录一个累加和最早出现的位置,所以此时什么记录也不加。

3、继续遍历下一个元素,直到所有的元素遍历完。

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

比如,数组[1,2,3,3],k = 6。如果从0位置开始累加,也就是遍历之前不加入(0,-1)记录,得到的结果会是2,而不是3。

int maxlength(vector<int> vec, int k)
{
	if (vec.size() == 0) return 0;
	std::map<int, int> mp;
	mp.insert(pair<int, int>(0, -1));
	int len = 0;
	int sum = 0;
	for (int i = 0; i < vec.size(); i++)
	{
		sum += vec[i];
		
		if (mp.find(sum - k) != mp.end())
		{
			len = max(i - mp[sum - k], len);
		}
		if (mp.find(sum) == mp.end())
		{
			mp.insert(pair<int, int>(sum, i));
		}
	}
	return len;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值