算法入门_求最大连续子列和的四种解法
0.简介
刚开始学算法(看视频),遇到个入门算法问题:求最大连续子列的和。
#include<stdio.h>
#include<string.h>
int MaxContiSubSum(int str[],int n);
int main()
{
int str[]={33,20,-60,40,-50,20,-25,35,-10,34};
int sum;
//Maximal continuous subset sum
sum = MaxContiSubSum(str,10);
printf("Maximal continuous subset sum = %d\n",sum);
return 0;
}
int MaxContiSubSum(int str[],int n)//让我们写的函数
{
;
}
卡了很久,记录一下,特别是分而治之的思想让我想了好久!
我也发现这四个算法有个缺点,就是所求子列和必须是正的,不然就直接是0了。
这四个算法复杂度依次降低
编译环境:VC++6.0
1.算法一:暴力运算累加
三个循环,第三个循环求前两个循环区间段的子列和,然后作比较判断。
复杂度:O(N^3)
int MaxContiSubSum(int str[],int n)
{
int a,b;//记录区间段序列
int i,j,k;
int ThisSum,MaxSum = 0;
for(i=0;i<n;i++)
{
for(j=i;j<n;j++)
{
ThisSum = 0;
for(k=i;k<=j;k++)//循环 求i到j的子列的和。
ThisSum += str[k];
if(ThisSum > MaxSum)//判断
{
MaxSum = ThisSum;
a=i;
b=j;
}
}
}
printf("i = %d , j = %d\n",a,b);//输出连续子列的序号
return MaxSum;
}
2.算法二:选择排序累加
两个循环,类似选择排序,第二个循环的同时,依次求i到j的子列的和。
复杂度:O(N^2)
int MaxContiSubSum(int str[],int n)
{
int ThisSum,MaxSum = 0;
int a,b;//记录区间段序列
int i,j;
for(i=0;i<n;i++)
{
ThisSum = 0;
for(j=i;j<n;j++)
{
ThisSum += str[j];//循环同时 依次 求i到j的子列的和。
if(ThisSum > MaxSum)//判断
{
MaxSum = ThisSum;
a = i;
b = j;
}
}
}
printf("i = %d ,j = %d\n",a,b);
return MaxSum;
}
3.算法三:分而治之
分而治之,基于递归。
有三个部分:一个完整数组从中间分为两部分,左端和右端。求出左端最大连续子列和MaxLeftSum,求出右端最大连续子列和MaxRightSum。第三部分为经过中点分界线的连续子列,求出其和(MaxLeftBorderSum + MaxRightBorderSum)。最后比较三者得出最大值。
当然,求左端(右端)最大连续子列的方法就用到递归。你可以想象为直接递归到求str[0]和str[1]两个值,然后按分而治之思想再去求两个值的最大连续子列和。然后将str[0]和str[1]看作左端,用分而治之思想求str[0],str[1],str[2],str[3]的最大连续子列和。然后0…….
个人认为,别管有没有理解透彻,先照着写一遍,先上手。
复杂度:O(NlogN)
//保持与前两种算法相同的函数接口。
int MaxContiSubSum(int str[],int n)
return DivideAndConquer(str,0,n-1);
//分而治之算法主体,基于递归。
int DivideAndConquer(int str[],int left,int right)
{
int MaxLeftSum,MaxRightSum;//分别存放左右端最大连续子列和。
int MaxLeftBorderSum=0,MaxRightBorderSum=0;//存放跨分界线最大连续子列和
int LeftBorderSum=0,RightBorderSum=0;
int center,i;
if(left == right)//递归的终止条件,最小子列存放一个数字
if(str[left] > 0)
return str[left];
else
return 0;
//下面是“分”的过程
center = (left + right)/2;//中点,分界点
//递归 求左右两端的最大连续子列和。
MaxLeftSum = DivideAndConquer( str , left , center);
MaxRightSum = DivideAndConquer( str , center+1 , right);
//求跨过分界线的最大连续子列。
for(i=center ; i>=left && str[i]>0; i--)//从分界线向左扫描
{
LeftBorderSum += str[i];
if(LeftBorderSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
}
for(i=center+1 ; i<=right && str[i]>0 ; i++)//从分界线向右扫描
{
RightBorderSum +=str[i];
if(RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
}
//下面是“治”的结果
return Max3(MaxLeftSum , MaxRightSum ,MaxRightBorderSum + MaxLeftBorderSum);
}
int Max3(int A , int B , int C)
{
return A>B?A>C?A:C:B>C?B:C;
//return A>B?(A>C?A:C):(B>C?B:C);
}
4.算法四:在线处理
特点就是,只要遇到当前子列和是负的,就直接舍弃,因为我们要求的是连续最大子列和。
复杂度:O(N)
int MaxContiSubSum(int str[],int n)
{
int ThisSum=0, MaxSum=0;
int i;
for(i=0 ; i<n ; i++)
{
ThisSum += str[i];
if(ThisSum > MaxSum)
MaxSum = ThisSum;
else if(ThisSum < 0)
ThisSum = 0;
}
return MaxSum;
}