子数组最大和问题

一、问题描述

输入:一个整型数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。

例如:

输入的数组为1, -2, 3, 10, -4, 7, 2, -5

则最大的子数组为3, 10, -4, 7, 2,和为18

 

二、问题分析

1.只要求出最大子数组的起始位置和结束位置,求和则很简单。不难看出,最大子数组有个特点:起始位置和结束位置的数一定是正数,而且起始位置之前如果还有数,此数一定不是正数,否则我们可以把它也包含进来,这样和会更大,同理结束位置之后如果有数,那么也一定不是正数。

2.我们可以先将问题简化为这样一个问题:输入的数组是正负交替的;如果不是,我们可以很轻松地将其变成正负交替的:将连续的正数/负数求和成为一个新的正数/负数即可。

如果输入数组是这样的:+ - + - + - +,那么我们可以这样来解决这个问题:

遍历这个数组中的数:首先a[0]将是我们最大的子数组;当遍历到a[1]时不会改变什么;当遍历到a[2]时,最大的子数组将可能产生变化:1.保持不变、2.变为a[2]、3.a[0]+a[1]+a[2]。这很简单,只要比较这三个数哪个最大即可,然后将最大的结果保存为我们新的最大子数组,继续遍历;这时后面的问题基本就是这个步骤的重复,但是有一点需要注意:并不是每当到下一个正数时都会有三种情况,因为我们要保证子数组必须是连续的,所以若a[0]仍然是最大的,则此时它将不能和后续的数串联起来,因此将只有两种情形来比较大小。

我们需要创建一个结构体来存储我们的结果,这个结构体应该包括四个参数:

int start--表示最大子数组的起始坐标

int end--表示最大子数组的结束坐标

int sum--子数组的和

int flag--标志此子数组是否可扩展:1代表可扩展,0代表不能扩展(可扩展意味着目前的子数组与即将遍历到的负数和正数是连续的)

对于我们这个简化的正负数交替的输入数组模型,相应的算法流程可以表示如下:

1.初始化最大子数组为第一个出现的正数:a[0]或a[1],取决于孰正孰负,同时需要置标志为1--可扩展

2.继续遍历数组,当遇到负数时跳过,遇到正数时,则讨论:若最大子数组标志位为1,则比较最大子数组和、当前a[i],以及最大子数组和+当前a[i]+a[i-1],谁最大,则谁是最大子数组;若标志位为0,则只比较最大子数组和、当前a[i]。同时还需保留新的最大数组,并置标志位。

#include <stdio.h>

typedef struct
{
	int start_pos;		//最大子数组起始下标
	int end_pos;		//最大子数组结束下标
	int sum;			//最大子数组和
	int flag;			//标志位:1--子数组可扩展;0--子数组不可扩展
}max_sub_struct;

max_sub_struct max_sub;

void get_max_sub(int a[], int n)
{
	int i;
	int index;

	i = initial(a);	
	for ( ; i < n; i ++)
	{
		if (a[i] < 0)
		{
			continue;
		}
		if (max_sub.flag)
		{
			index = compare_three_nums(max_sub.sum,a[i],a[i]+a[i-1]+max_sub.sum);
			if (index == 1)
			{
				max_sub.flag = 0;
			}else if (index == 2)
			{
				max_sub.start_pos = i;
				max_sub.end_pos = i;
				max_sub.sum = a[i];
				max_sub.flag = 1;
			}else
			{
				max_sub.end_pos = i;
				max_sub.sum = a[i]+a[i-1]+max_sub.sum;
				max_sub.flag = 1;
			}
		}else
		{
			if(max_sub.sum < a[i])
			{
				max_sub.start_pos = i;
				max_sub.end_pos = i;
				max_sub.sum = a[i];
				max_sub.flag = 1;
			}else
			{
				max_sub.flag = 0;
			}
		}
	}
}

/*
*初始化函数,a[0]或a[1]为正,将其保存到max_sub中,并为遍历数组的初始位置赋值
*/
int initial(int a[])
{
	int i;
	if(a[0] > 0)
	{
		max_sub.start_pos = 0;
		max_sub.end_pos = 0;
		max_sub.sum = a[0];
		i = 1;
	}else
	{
		max_sub.start_pos = 1;
		max_sub.end_pos = 1;
		max_sub.sum = a[1];
		i = 2;
	}
	max_sub.flag = 1;
	return i;
}
/*
*比较三个数,a最大则返回1;b最大则返回2;c最大则返回3
*/
int compare_three_nums(int a, int b, int c)
{
	int max_num  = a;
	if(max_num < b)
	{
		max_num = b;
	}
	if(max_num < c)
	{
		max_num = c;
	}
	if(max_num == c)
	{
		return 3;
	}
	if(max_num == b)
	{
		return 2;
	}
	if(max_num == a)
	{
		return 1;
	}
}

/*
*格式化输出max_sub
*/
void display_max_sub(void)
{
	printf("max_sub.start_pos = %d\n",max_sub.start_pos);
	printf("max_sub.end_pos = %d\n",max_sub.end_pos);
	printf("max_sub.sum = %d\n",max_sub.sum);
}

int main()
{
	int a[] = {-2,13,-4,9,-5};
	get_max_sub(a,5);
	display_max_sub();

	system("pause");
	return 0;
}


未完待续…………

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值