算法学习-零子数组,最大连续子数组

题目

对于长度为N的数组A,求连续子数组的和最接近0的值。

如:

数组A:1,-2,3,10,-4,7,2,-5

它是所有子数组中,和最接近0的是哪个?

算法流程

申请比A长1的空间sum[-1,0,...,N-1],sum[i]是A的前i项和。定义sum[-1]=0

显然有:A的i到j项和=sum(j)-sum(i-1)

算法思路:

对sum[-1,0,...,N-1]排序,然后计算sum相邻元素的差的绝对值,最小即为所求

在A中任意取两个前缀子数组的和,求差的最小值。

讨论

计算前n项和数组sum和计算sum相邻元素差的时间复杂度,都是O(N),排序的时间复杂度认为是O(N*logN),因此,总时间复杂度:O(NlogN)。

思考:如果需要返回绝对值最小的子数组本身呢?

代码如下

int MinSubarray(const int* a, int size)
{
	int* sum = new int[size + 1];
	sum[0] = 0;
	int i;
	for (i = 0; i < size; i++)
	{
		sum[i + 1] = sum[i] + a[i];
	}
	sort(sum, sum + size + 1);
	int difference = abs(sum[1] - sum[0]);
	int result = difference;
	for (i = 1; i < size; i++)
	{
		difference = abs(sum[i + 1] - sum[i]);
		result = min(difference, result);
	}
	delete[] sum;
	return result;
}

下面来看一下最大连续子数组的问题

给定一个数组A[0,...,n-1],求A的连续子数组,使得该子数组的和最大。

例如:数组1,-2,3,10,-4,7,2,-5最大子数组:3,10,-4,7,2

分析

记S[i]为以A[i]结尾的数组中和最大的子数组,则:S[i+1]=max(S[i]+A[i+1],A[i+1])

S[0]=A[0]

遍历i:0<=i<=n-1

动态规划:最优子问题

时间复杂度O(n)

代码如下

int MaxSubarray(const int* a, int size)
{
	if (!a || (size <= 0))
		return 0;

	int sum = a[0];  // 当前子串的和
	int result = sum;  // 当前找到的最优解
	for (int i = 1; i < size; i++)
	{
		if (sum > 0)
		{
			sum += a[i];
		}
		else
		{
			sum = a[i];
		}
		result = max(sum, result);
	}
	return result;
}

求最大子数组还有一种思路如下:

定义:前缀和sum[i]=a[0]+a[1]+...+a[i]则:a[i,j]=sum[j]-sum[i-1](定义p[-1]=0),最大子数组=sum(j)-sum(i-1)

算法过程

  • 求i前缀sum[i]:
    • 遍历i:0<=i<=n-1
    • sum[i]=sum[i-1]+a[i]
  • 计算以a[j]结尾的子数组的最大值
    • 对于某个j:遍历0<=i<=j,求sum[i]的最小值m
    • sum[j]-m即为a[j]结尾的数组中最大的子数组的值
  • 统计sum[j]-m的最大值,0<=j<=n-1
    • 1,2,3步都是线性的,因此,时间复杂度O(n)。

如果要求子数组本身,只需要记录一下from和to就可以了






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值