最大子列和

问题

在计算机科学中,最大子数列问题的目标是在数列的一维方向找到一个连续的子数列,使该子数列的和最大。例如,对一个数列 −2, 1, −3, 4, −1, 2, 1, −5, 4,其连续子数列中和最大的是 4, −1, 2, 1, 其和为6。

对应习题

最大子列和(中文)

解决方法

穷举法

穷举法这一种方法简单直接粗暴,容易理解。但是它的缺点是效率很低,复杂度是O(n^3)。
穷举法的思路是:枚举左端点和右端点也就是i,j,需要O(n^2)。然后计算i到j这个子列的和,又需要O(n).
复杂度:O(n^3)

int MaxSubSeqSum1(int List[], int n)
{
	int MaxSum = 0, ThisSum;
	for (int i = 0; i < n; i++)
	{
		for (int j = i; j < n; j++)
		{
			ThisSum = 0;
			for (int k = i; k <= j; k++)
			{
				ThisSum += List[k];
			}
			if (ThisSum > MaxSum)
			{
				MaxSum = ThisSum;
			}
		}
	}
	return MaxSum;
}

部分穷举

复杂度:O(n^2)。
效率提高在穷举法的第三重循环,第三重循环不需要从头到尾加起来。因为可以在第二个循环的时候,将前面的结果累加起来,就不需要再从最开始就累加。

int MaxSubSeqSum2(int List[], int n)//部分穷举
{
	int ThisSum, MaxSum = 0;
	for (int i = 0; i < n; i++)
	{
		ThisSum = 0;
		for (int j = i; j < n; j++)
		{
			ThisSum += List[j];
		}
		if (ThisSum > MaxSum)
		{
			MaxSum = ThisSum;
		}
	}
	return MaxSum;
}

分而治之

分而治之的基本思路就是将一个问题细分为若干小问题,分别解决之后再合而治之。
如下图,他的基本解决思路是将序列从中分为左右两个序列,递归求两个子列的最大和,左子列最大和和右子列最大和,然后从中间扫描找出跨越边界的最大子列和,最后求三者中的最大的那个数。他的核心是递归。
复杂度:O(nlogn)

在这里插入图片描述

int Max(int a, int b, int c)
{
	return a > b ? (a > c ? a : c) : (b > c ? b : c);
}
int Divide(int List[], int Left, int Right)
{
	int MaxLeftSum=0, MaxRightSum=0;
	int MaxLeftBorderSum=0, MaxRightBorderSum=0;
	int LeftBorderSum = 0, RightBorderSum = 0;
	int center = (Left + Right) / 2;
	if(Left==Right)
	//if (List[Left] == List[Right])
	{
		if (List[Left] > 0) return List[Left];//递归终止的条件
		else return 0;
	}
	MaxLeftSum = Divide(List, Left, center);
	MaxRightSum = Divide(List, center + 1, Right);
	for (int i = center; i >= Left; i--)
	{
		LeftBorderSum += List[i];
		if (LeftBorderSum > MaxLeftBorderSum)
		{
			MaxLeftBorderSum = LeftBorderSum;
		}
	}
	for (int i = center+1;i<=Right;i++)
	{
		RightBorderSum += List[i];
		if (RightBorderSum> MaxRightBorderSum)
		{
			MaxRightBorderSum = RightBorderSum;
		}
	}
	return Max(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);
}
int MaxSubSeqSum3(int List[], int n)//分而治之
{
	return Divide(List, 0, n - 1);
}

在线处理

在线处理的意思就是每输入一个数据就马上进行处理。
在线处理的基本思想就是既然我们求的是一个最大子列和问题,如果在我们求的过程中,前面的子列和为负,这样就会影响求后面的子列和。所以我们就可以将其舍弃,将其置为0重新选择子列和进行累加。
o902复杂度:O(n)

int MaxSubSeqSum4(int  List[], int n)
{
	int ThisSum=0, MaxSum=0;
	for (int i = 0; i < n; i++)
	{
		ThisSum += List[i];
		if (ThisSum > MaxSum)
		{
			MaxSum = ThisSum;
		}
		else if (ThisSum < 0)
		{
			ThisSum = 0;
		}
	}
	return MaxSum;
}

实现源码

#include<iostream>
using  namespace std;
#define MAX 100000
int MaxSubSeqSum1(int List[], int n)
{
	int MaxSum = 0, ThisSum;
	for (int i = 0; i < n; i++)
	{
		for (int j = i; j < n; j++)
		{
			ThisSum = 0;
			for (int k = i; k <= j; k++)
			{
				ThisSum += List[k];
			}
			if (ThisSum > MaxSum)
			{
				MaxSum = ThisSum;
			}
		}
	}
	return MaxSum;
}
int MaxSubSeqSum2(int List[], int n)//部分穷举
{
	int ThisSum, MaxSum = 0;
	for (int i = 0; i < n; i++)
	{
		ThisSum = 0;
		for (int j = i; j < n; j++)
		{
			ThisSum += List[j];
			if (ThisSum > MaxSum)
			{
				MaxSum = ThisSum;
			}
		}
	}
	return MaxSum;
}
int Max(int a, int b, int c)
{
	return a > b ? (a > c ? a : c) : (b > c ? b : c);
}
int Divide(int List[], int Left, int Right)
{
	int MaxLeftSum=0, MaxRightSum=0;
	int MaxLeftBorderSum=0, MaxRightBorderSum=0;
	int LeftBorderSum = 0, RightBorderSum = 0;
	int center = (Left + Right) / 2;
	if(Left==Right)
	//if (List[Left] == List[Right])
	{
		if (List[Left] > 0) return List[Left];//递归终止的条件
		else return 0;
	}
	MaxLeftSum = Divide(List, Left, center);
	MaxRightSum = Divide(List, center + 1, Right);
	for (int i = center; i >= Left; i--)
	{
		LeftBorderSum += List[i];
		if (LeftBorderSum > MaxLeftBorderSum)
		{
			MaxLeftBorderSum = LeftBorderSum;
		}
	}
	for (int i = center+1;i<=Right;i++)
	{
		RightBorderSum += List[i];
		if (RightBorderSum> MaxRightBorderSum)
		{
			MaxRightBorderSum = RightBorderSum;
		}
	}
	return Max(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);
}
int MaxSubSeqSum3(int List[], int n)//分而治之
{
	return Divide(List, 0, n - 1);
}
int MaxSubSeqSum4(int  List[], int n)
{
	int ThisSum=0, MaxSum=0;
	for (int i = 0; i < n; i++)
	{
		ThisSum += List[i];
		if (ThisSum > MaxSum)
		{
			MaxSum = ThisSum;
		}
		else if (ThisSum < 0)
		{
			ThisSum = 0;
		}
	}
	return MaxSum;
}
int main()
{
	int n;
	int List[MAX] = { 0 };
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> List[i];
	}
	cout << MaxSubSeqSum1(List, n) << endl;
	cout << MaxSubSeqSum2(List, n) << endl;
	cout << MaxSubSeqSum3(List, n) << endl;
	cout << MaxSubSeqSum4(List, n) << endl;
	return 0;
}

测试结果

在这里插入图片描述

总结

1.令相同功能的函数具有相同的接口是个好习惯,没有这个习惯的孩子不是好孩子。
2.求最大子列和问题还有动态规划的解决方法,但是我看不懂(o(╥﹏╥)o)

后记

参考了陈越姥姥的数据结构,接下来的时间主要学这个了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值