最大子数组和(最大子段和)

简介:

比如对于数组[1,-2,3,5,-1,2] 最大子数组和是sum[3,5,-1,2] = 9, 我们要求函数输出子数组和的最大值,并且返回子数组的左右边界(下面函数的left和right参数).

本文我们规定当数组中所有数都小于0时,返回数组中最大的数(也可以规定返回0,只要让以下代码中maxsum初始化为0即可,此时我们要注意-1 0 0 0 -2这种情形,特别是如果要求输出子数组的起始位置时,如果是面试就要和面试官问清楚)

以下代码我们在PAT 1007. Maximum Subsequence Sum测试通过,测试main函数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
int  main()
{
     int  n;
     scanf ( "%d" , &n);
     vector< int >vec(n);
     for ( int  i = 0; i < n; i++)
         scanf ( "%d" , &vec[i]);
     int  left, right;
     int  maxsum = maxSum1(vec, left, right); //测试时替换函数名称
     if (maxsum >= 0)
         printf ( "%d %d %d" , maxsum, vec[left], vec[right]);
     else  printf ( "0 %d %d" , vec[0], vec[n-1]);
}

参考:编程之美2.14 求数组的子数组之和的最大值

 

算法1:最简单的就是穷举所有的子数组,然后求和,复杂度是O(n^3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int  maxSum1(vector< int >&vec, int  &left, int  &right)
{
     int  maxsum = INT_MIN, sum = 0;
     for ( int  i = 0; i < vec.size(); i++)
         for ( int  k = i; k < vec.size(); k++)
         {
             sum = 0;
             for ( int  j = i; j <= k; j++)
                 sum += vec[j];
             if (sum > maxsum)
             {
                 maxsum = sum;
                 left = i;
                 right = k;
             }
         }
     return  maxsum;
}

算法2: 上面代码第三重循环做了很多的重复工作,稍稍改进如下,复杂度为O(n^2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int  maxSum2(vector< int >&vec, int  &left, int  &right)
{
     int  maxsum = INT_MIN, sum = 0;
     for ( int  i = 0; i < vec.size(); i++)
     {
         sum = 0;
         for ( int  k = i; k < vec.size(); k++)
         {
             sum += vec[k];
             if (sum > maxsum)
             {
                 maxsum = sum;
                 left = i;
                 right = k;
             }
         }
     }
     return  maxsum;
}

算法3: 分治法, 下面贴上编程之美的解释, 复杂度为O(nlogn)

image

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//求数组vec【start,end】的最大子数组和,最大子数组边界为[left,right]
int  maxSum3(vector< int >&vec, const  int  start, const  int  end, int  &left, int  &right)
{
     if (start == end)
     {
         left = start;
         right = left;
         return  vec[start];
     }
     int  middle = start + ((end - start)>>1);
     int  lleft, lright, rleft, rright;
     int  maxLeft = maxSum3(vec, start, middle, lleft, lright); //左半部分最大和
     int  maxRight = maxSum3(vec, middle+1, end, rleft, rright); //右半部分最大和
     int  maxLeftBoeder = vec[middle], maxRightBorder = vec[middle+1], mleft = middle, mright = middle+1;
     int  tmp = vec[middle];
     for ( int  i = middle-1; i >= start; i--)
     {
         tmp += vec[i];
         if (tmp > maxLeftBoeder)
         {
             maxLeftBoeder = tmp;
             mleft = i;
         }
     }
     tmp = vec[middle+1];
     for ( int  i = middle+2; i <= end; i++)
     {
         tmp += vec[i];
         if (tmp > maxRightBorder)
         {
             maxRightBorder = tmp;
             mright = i;
         }
     }
     int  res = max(max(maxLeft, maxRight), maxLeftBoeder+maxRightBorder);
     if (res == maxLeft)
     {
         left = lleft;
         right = lright;
     }
     else  if (res == maxLeftBoeder+maxRightBorder)
     {
         left = mleft;
         right = mright;
     }
     else
     {
         left = rleft;
         right = rright;
     }
     return  res;
}

算法4: 动态规划, 数组为vec[],设dp[i] 是以vec[i]结尾的子数组的最大和,对于元素vec[i+1], 它有两种选择:a、vec[i+1]接着前面的子数组构成最大和,b、vec[i+1]自己单独构成子数组。则dp[i+1] = max{dp[i]+vec[i+1],  vec[i+1]}

如果不考虑记录最大子数组的位置,于是有以下代码:                本文地址

 

1
2
3
4
5
6
7
8
9
10
int  maxSum_(vector< int >&vec)
{
     int  maxsum = INT_MIN, sum = 0;
     for ( int  i = 0; i < vec.size(); i++)
     {
         sum = max(sum + vec[i], vec[i]);
         maxsum = max(maxsum, sum);
     }
     return  maxsum;
}

 

对以上代码换个写法,并记录最大子数组的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int  maxSum4(vector< int >&vec, int  &left, int &right)
{
     int  maxsum = INT_MIN, sum = 0;
     int  begin = 0;
     for ( int  i = 0; i < vec.size(); i++)
     {
         if (sum >= 0)
         {
             sum += vec[i];
         }
         else
         {
             sum = vec[i];
             begin = i;
         }
 
         if (maxsum < sum)
         {
             maxsum = sum;
             left = begin;
             right = i;
         }
     }
     return  maxsum;
}

如果数组是循环的,该如何呢

这时分两种情形(图中红色方框表示求得的最大子数组,left、right分别是子数组的开始和结尾):

(1)如下图最大的子数组没有跨过vec[n-1]到vec[0], 这就是每循环的情况

image

(2)如下图,最大的子数组跨过vec[n-1]到vec[0]

image

对于第二种情形,相当于从原数组中挖掉了一块(vec[right+1], …, vec[left-1]) ,那么我们只要使挖掉的和最小即可,求最小子数组和最大子数组类似,代码如下,以下代码在九度oj1572首尾相连数组的最大子数组和通过测试(测试需要,以下代码当数组全是负数时,输出0):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int  maxSumCycle(vector< int >&vec, int  &left, int &right)
{
     int  maxsum = INT_MIN, curMaxSum = 0;
     int  minsum = INT_MAX, curMinSum = 0;
     int  sum = 0;
     int  begin_max = 0, begin_min = 0;
     int  minLeft, minRight;
     for ( int  i = 0; i < vec.size(); i++)
     {
         sum += vec[i];
         if (curMaxSum >= 0)
         {
             curMaxSum += vec[i];
         }
         else
         {
             curMaxSum = vec[i];
             begin_max = i;
         }
 
         if (maxsum < curMaxSum)
         {
             maxsum = curMaxSum;
             left = begin_max;
             right = i;
         }
         ///////////////求和最小的子数组
         if (curMinSum <= 0)
         {
             curMinSum += vec[i];
         }
         else
         {
             curMinSum = vec[i];
             begin_min = i;
         }
 
         if (minsum > curMinSum)
         {
             minsum = curMinSum;
             minLeft = begin_min;
             minRight = i;
         }
     }
     if (maxsum >= sum - minsum)
         return  maxsum;
     else
     {
         left = minRight+1;
         right = minLeft-1;
         return  sum - minsum;
     }
}





本文转自tenos博客园博客,原文链接:http://www.cnblogs.com/TenosDoIt/p/3698246.html,如需转载请自行联系原作者

目录
相关文章
|
机器学习/深度学习 存储 人工智能
50道必备的Python面试题 (建议点赞)
50道必备的Python面试题 (建议点赞)
8259 0
|
算法 测试技术 C++
【洛谷刷题】蓝桥杯专题突破-广度优先搜索-bfs(13)
【洛谷刷题】蓝桥杯专题突破-广度优先搜索-bfs(13)
156 0
【洛谷】【动态规划】P1002 [NOIP2002 普及组] 过河卒
【洛谷】【动态规划】P1002 [NOIP2002 普及组] 过河卒
509 0
【洛谷】【动态规划】P1002 [NOIP2002 普及组] 过河卒
|
8天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
7天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
本文讲解 Prompt 基本概念与 10 个优化技巧,结合学术分析 AI 应用的需求分析、设计方案,介绍 Spring AI 中 ChatClient 及 Advisors 的使用。
348 130
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
|
19天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1332 8
|
7天前
|
人工智能 Java API
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)
本文介绍AI大模型的核心概念、分类及开发者学习路径,重点讲解如何选择与接入大模型。项目基于Spring Boot,使用阿里云灵积模型(Qwen-Plus),对比SDK、HTTP、Spring AI和LangChain4j四种接入方式,助力开发者高效构建AI应用。
336 122
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)