算法入门_求最大连续子列和的四种解法

算法入门_求最大连续子列和的四种解法

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;

}	
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值