问题
在计算机科学中,最大子数列问题的目标是在数列的一维方向找到一个连续的子数列,使该子数列的和最大。例如,对一个数列 −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)
后记
参考了陈越姥姥的数据结构,接下来的时间主要学这个了。