中国大学慕课——浙江大学数据结构 关于最大子列和的四种算法及复杂度

一开始就上最大子列和,的确不太友好…
在这里插入图片描述
法一:暴力 每次从头往后加

int maxsubsum1(int a[], int n)
{
	for (int i = 0; i < n; i++)
	{
		for (int j = i; j < n; j++)
		{
			int this_sum = 0;
			for (int k = i; k <= j; k++)
			{
				this_sum += a[k];
			}
			if (this_sum > max_sum)
			{
				max_sum = this_sum;
			}
		}
	}
	return max_sum;
}

复杂度很明显了,三层for循环 O(N^3)

法二:做了一点小小的改进 每次计算时,没必要从头往后加,只用在之前序列的末尾再加上下一个数就OK

int maxsubsum2(int a[], int n)
{
	for (int i = 0; i < n; i++)
	{
		int this_sum = 0;
		for (int j = i; j < n; j++)
		{
			this_sum += a[j];
		}
		if (this_sum > max_sum)
		{
			max_sum = this_sum;
		}
	}
	return max_sum;
}

复杂度也很明显了,两个for循环,O(N^2)

注意:当发现算法时间复杂度为O(N^2)时,下意识改进为O(NlogN)

法三:分治(自己写没写出来,直接用给出的代码了)
基本思想:递归地对左边求最大子列->递归地对右边求最大子列和->求跨越中间求最大子列和->最后取最大的那个

int Max3(int A, int B, int C)
{ /* 返回3个整数中的最大值 */
	return A > B ? A > C ? A : C : B > C ? B : C;
}
int DivideAndConquer(int List[], int left, int right)
{ /* 分治法求List[left]到List[right]的最大子列和 */
	int MaxLeftSum, MaxRightSum; /* 存放左右子问题的解 */
	int MaxLeftBorderSum, MaxRightBorderSum; /*存放跨分界线的结果*/

	int LeftBorderSum, RightBorderSum;
	int center, i;

	if (left == right)  
	{ /* 递归的终止条件,子列只有1个数字 */
		if (List[left] > 0)  return List[left];
		else return 0;
	}

	/* 下面是"分"的过程 */
	center = (left + right) / 2; /* 找到中分点 */
	/* 递归求得两边子列的最大和 */
	MaxLeftSum = DivideAndConquer(List, left, center);
	MaxRightSum = DivideAndConquer(List, center + 1, right);

	/* 下面求跨分界线的最大子列和 */
	MaxLeftBorderSum = 0; LeftBorderSum = 0;
	for (i = center; i >= left; i--)
	{ /* 从中线向左扫描 */
		LeftBorderSum += List[i];
		if (LeftBorderSum > MaxLeftBorderSum)
			MaxLeftBorderSum = LeftBorderSum;
	} /* 左边扫描结束 */

	MaxRightBorderSum = 0; RightBorderSum = 0;
	for (i = center + 1; i <= right; i++) 
	{ /* 从中线向右扫描 */
		RightBorderSum += List[i];
		if (RightBorderSum > MaxRightBorderSum)
			MaxRightBorderSum = RightBorderSum;
	} /* 右边扫描结束 */

	/* 下面返回"治"的结果 */
	return Max3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);
}
int maxsubsum3(int List[], int N)
{ /* 保持与前2种算法相同的函数接口 */
	return DivideAndConquer(List, 0, N - 1);
}

法四:在线处理
基本思想:当前子列的和如果为负,则将它抛弃。因为加上一个负数,只会使后面的数越加越小
详细讲解可以去看看视频,上面用动画显示出来,超级直观

int maxsubsum4(int a[], int n)
{
	int this_sum = 0;
	for (int i = 0; i < n; i++)
	{
		this_sum += a[i];
		if (this_sum > max_sum)
		{
			max_sum = this_sum;
		}
		else if (this_sum < 0)
		{
			this_sum = 0;
		}
	}
	return max_sum;
}

复杂度为O(N) 线性复杂度

例:
在这里插入图片描述
用了一下之前比赛学的代码,AC了:

#include <iostream>
#include <algorithm>
using namespace std;
const int inf = 0x7fffffff;
int num[100005];

int main()
{
	int N;
	cin >> N;
	for (int i = 0; i<N; i++)
	{
		cin >> num[i];
	}
	int ans = -inf;
	for (int i = 0; i<N; i++)
	{
		ans = max(ans, num[i]);
	}
	if (ans <= 0)
	{
		cout << 0 << endl;
	}
	else
	{
		int sum = 0;
		for (int i = 0; i<N; i++)
		{
			if (sum + num[i]<0)
			{
				sum = 0;
			}
			else
			{
				sum += num[i];
			}
			ans = max(ans, sum);
		}
		cout << ans << endl;
	}
	return 0;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值