一、问题描述
输入:一个整型数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为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;
}
未完待续…………