【难】求数组(包括正负数和零)中相加的和(小于或)等于k的最长子数组的长度

题目:来自脑客爱刷题

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

解法要求:时间复杂度O(N),额外空间复杂度O(N)

int getSumKMaxSubArrayLength(int* num, int length, int k)
{
	if (num == nullptr || length <= 0 || k <= 0)
		return -1;
	unordered_map<int, int> hashmap;
	hashmap.emplace(0, -1);
	int sum = 0, len = -1;
	for (int i = 0; i < length; i++)
	{
		sum += num[i];
		if (hashmap.find(sum-k) != hashmap.end())//哈希表的查找操作是O(1)
		{
			if (i - hashmap[sum-k]>len)
				len = i - hashmap[sum - k] ;
		}
		if (hashmap.find(sum) == hashmap.end())
		{
			hashmap[sum] = i;
		}

	}
	return len;
}


进阶1:给定一个无序数组arr,arr中元素可以是正数、负数和0,求正数和负数的个数相等的最长子数组的长度。

提示:将题目中的正数变成1,负数变成-1,则代入原来题目的代码,求和为0的最长子数组的长度。

int equal_number_of_positive_and_negative(int* num, int length)
{
	if (num == nullptr || length <= 0)
		return -1;
	unordered_map<int, int> hashmap;
	hashmap.emplace(0, -1);
	int sum = 0, len = -1;
	for (int i = 0; i < length; i++)
	{
		if (num[i]>0)
			sum += 1;
		else if (num[i]<0)
			sum += -1;
		if (hashmap.find(sum)!= hashmap.end())//哈希表的查找操作是O(1)
		{
			if (i - hashmap[sum]>len)
				len = i - hashmap[sum];
		}
		else//if (hashmap.find(sum) == hashmap.end())
		{
			hashmap[sum] = i;
		}

	}
	return len;
}

进阶2:给定一个无序数组arr,arr中元素只能是0或1,求0和1个数相等的最长子数组的长度。

提示:把数组中的0变成-1,则题目就变成进阶1一样的题目。


进阶3:

题目:EPI


提示:
设数组S,其中S[i]为数组A从0到i位的数的和。
在第i位,有S[i],则需要从S数组的右边开始,寻找第一个小于等于S[i]+k的数,假设这个数是S[j]。则A[i+1:j符合题目的要求——相加之和小于等于k。为了使查找达到O(logn)的时间复杂度,则创建一个有序数组minS,利用二分查找寻找S[j]的下标j。时间复杂度O(nlogn),空间复杂度O(n)。

//在数组arr中小于等于x的数里面,找到其中下标最大那个数的下标
int find_last_smaller_equal_x(const vector<int> &arr, const int x)
{
	int len = arr.size(),left=0,right=len-1;
	int index = -1;
	while (left <= right)
	{
		if (left == right)
		{
			if (arr[left]<=x)
				index = max(index, left);
			break;
		}
		int mid = left + (right - left) / 2;
		if (arr[mid] <= x)
		{
			index = max(index, mid);
			left = mid + 1;
		}
		else
			right = mid - 1;
	}
	return index;
}

pair<int, int> find_sum_smaller_equal_k(const vector<int> &A,const int k)
{
	
	if (A.empty())
		return pair<int, int>(-1,-1);
	vector<int> prefix_sum(A.size(), 0);
	int sum = 0;
	for (int i = 0; i < A.size(); i++)
	{
		sum += A[i];
		prefix_sum[i] = sum;
	}
	vector<int> min_prefix_sum(prefix_sum);
	for (int i = A.size()-2; i>=0; i--)
	{
		min_prefix_sum[i] = min(min_prefix_sum[i], min_prefix_sum[i + 1]);
	}
	//第0位的情况特殊,应该先处理
	pair<int, int> res(0, -1);
	res.second = find_last_smaller_equal_x(min_prefix_sum,k);
	for (int i = 0; i < A.size(); i++)
	{
		int idx = find_last_smaller_equal_x(min_prefix_sum, prefix_sum[i] + k);
		if (idx != -1 && idx - i - 1 > res.second - res.first)
		{
			res.first = i + 1;
			res.second = idx;
		}
	}
	return res;
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值