1. 程式人生 > >演算法學習-零子陣列,最大連續子陣列

演算法學習-零子陣列,最大連續子陣列

題目

對於長度為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就可以了