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

2024-02-19 03:18

本文主要是介绍编程之美之2.14 求数组的子数组之和的最大值,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【题目】

一个有N个整数元素的一维数组(A[0],A[1],A[2],...A[n-1]),这个数组中当然有很多子数组,那么子数组之和的最大值是多少?

该子数组是连续的。

我们先来明确一下题意:

(1)子数组意味着是连续的。

(2)题目只需要求和,并不需要返回子数组的具体位置。

(3)数组的元素是整数,所以数组可能包含正整数,负整数或者零。

举几个例子:

数组:[1,-2,3,5,-3,2]返回8 

数组:[0,-2,3,5,-1,2]返回9

数组:[-9,-2,-3,-5,-3]返回8

【解法一】

设Sum[i,...,j]为数组A中第i个元素到第j个元素的和(0<=i<=j<n),遍历所有的Sum[i,...,j]。

对每个整数对,我们都要计算x[i...j]的总和,并检验该总和是否大于迄今为止的最大总和。

/*********************************
*   日期:2014-5-19
*   作者:SJF0115
*   题目: 2.14 求数组的子数组之和的最大值
*   来源:编程之美
**********************************/
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <limits.h>
using namespace std;#define N 1001int array[N];int main(){int n,i,j,k,sum;//freopen("C:\\Users\\XIAOSI\\Desktop\\acm.txt","r",stdin);while(scanf("%d",&n) != EOF){//输入数据for(i = 0;i < n;i++){scanf("%d",&array[i]);}int maxSum = INT_MIN;// i 遍历数组起点 j 遍历数组终点for(i = 0;i < n;i++){for(j = i;j < n;j++){sum = 0;//遍历所有可能的Sum[i..j]for(k = i;k <= j;k++){sum += array[k];}maxSum = max(maxSum,sum);}}printf("%d\n",maxSum);}return 0;
}

时间复杂度为O(n^3)。程序运行速度很慢。

【解法二】

x[i...j]的总和与前面已经计算出的x[i...j-1]的总和密切相关,Sum[i..j] = Sum[i...j-1]+array[j] 则可以将上面算法中最后一个循环去掉,

避免重复计算,从而使算法得以改进。

时间复杂度为O(n^2)。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <limits.h>
using namespace std;#define N 1001int array[N];int main(){int n,i,j,k,sum;//freopen("C:\\Users\\XIAOSI\\Desktop\\acm.txt","r",stdin);while(scanf("%d",&n) != EOF){//输入数据for(i = 0;i < n;i++){scanf("%d",&array[i]);}int maxSum = INT_MIN;// i 遍历数组起点 j 遍历数组终点for(i = 0;i < n;i++){sum = 0;for(j = i;j < n;j++){sum += array[j];maxSum = max(maxSum,sum);}}printf("%d\n",maxSum);}return 0;
}

【解法三】

通过访问外循环执行之前就以构建好的数据结构的方式在内循环中计算总和。

即常见的前缀和技巧。令Sumi=A0+A1+A2++Ai,规定Sum0=A0,则可以在O(1)时间内求出子序列的值:Ai+Ai+1+…+Aj=Sumj-Sumi-1。这样,时间复杂度降为O(n2)。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;#define N 1001int array[N];
int sum[N];
int main(){int n,i,j;//freopen("C:\\Users\\XIAOSI\\Desktop\\acm.txt","r",stdin);while(scanf("%d",&n) != EOF){//输入数据for(i = 0;i < n;i++){scanf("%d",&array[i]);//计算前缀和if(i != 0){sum[i] = sum[i-1] + array[i];}else{sum[i] = array[i];}}int maxSum = sum[0];// i 遍历数组起点 j 遍历数组终点for(i = 0;i < n;i++){for(j = i+1;j < n;j++){maxSum = max(maxSum,sum[j] - sum[i]);}}printf("%d\n",maxSum);}return 0;
}

【解法四】 分治策略

如果将所给数组(A[0],...,A[n-1])分为长度相等的两段数组(A[0],...,A[n/2-1])和(A[n/2],...,A[n-1]),分别求出这两段数组各自最大子段和,则原数组(A[0],...,A[n-1])的最大子段和分为以下三种情况,要么在前半部分a中,要么在后半部分b中,要么跨越a和b之间的边界:
          a.(A[0],...,A[n-1])的最大子段和与(A[0],...,A[n/2-1])的最大子段和相同;
          b.(A[0],...,A[n-1])的最大子段和与(A[n/2],...,A[n-1])的最大子段和相同;
          c.(A[0],...,A[n-1])的最大子段跨过其中间两个元素A[n/2-1]到A[n/2].
对应a和b两个问题是规模减半的两个相同的子问题,可以用递归求得。
对于c,需要找到以A[n/2-1]结尾的和最大的一段数组和S1=(A[i],...,A[n/2-1])和以A[n/2]开始和最大的一段和S2=(A[n/2],...,A[j]),那么第三种情况的最大值为S1+S2。只需要对原数组进行一次遍历即可。在a中的部分是a中包含右边界的最大子数组,在b中的部分是b中包含左边界的最大子数组。

这其实是一种分治策略,时间复杂度为O(nlogn)。

/*********************************
*   日期:2014-5-20
*   作者:SJF0115
*   题目: 2.14 求数组的子数组之和的最大值
*   来源:编程之美
**********************************/
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;#define N 1001int array[N];int MaxSubsequence(int L,int R){int i,j;//0个元素if(L > R){return 0;}//1个元素if(L == R){return array[L];}int mid = (L + R) / 2;//跨越a和b之间的部分//在a中的部分是a中包含右边界的最大子数组int sum = 0;int leftMax = array[mid];for(i = mid;i >= L;i--){sum += array[i];leftMax = max(leftMax,sum);}//在b中的部分是b中包含左边界的最大子数组sum = 0;int rightMax = array[mid+1];for(i = mid+1;i <= R;i++){sum += array[i];rightMax = max(rightMax,sum);}//递归调用//跨越a和bint maxSum = rightMax+leftMax;//整个在a中maxSum = max(maxSum,MaxSubsequence(L,mid));//整个在b中maxSum = max(maxSum,MaxSubsequence(mid+1,R));return maxSum;
}int main(){int n,i,j;//freopen("C:\\Users\\XIAOSI\\Desktop\\acm.txt","r",stdin);while(scanf("%d",&n) != EOF){//输入数据for(i = 0;i < n;i++){scanf("%d",&array[i]);}int maxSum = MaxSubsequence(0,n-1);printf("%d\n",maxSum);}return 0;
}


【解法五】

利用动态规划做题。

只遍历数组一遍,当从头到尾部遍历数组A, 遇到一个数有两种选择 (1)加入之前subArray (2)自己另起一个subArray

设状态S[i], 表示以A[i]结尾的最大连续子序列和,状态转移方程如下:

S[i] = max(S[i-1] + A[i],A[i])

时间复杂度:O(n)     空间复杂度:O(n)

【代码五】

    /*--------------------------------------------*   日期:2015-02-03*   作者:SJF0115*   题目: 53.Maximum Subarray*   网址:https://oj.leetcode.com/problems/maximum-subarray/*   结果:AC*   来源:LeetCode*   博客:-----------------------------------------------*/class Solution {public:int maxSubArray(int A[], int n) {int S[n];int maxSum = A[0];S[0] = A[0];// 动态规划for(int i = 1;i < n;i++){S[i] = max(S[i-1] + A[i],A[i]);if(S[i] > maxSum){maxSum = S[i];}//if}//forreturn maxSum;}};

【解法六】

对前一个方法继续优化,从状态转移方程上S【i】只与S【i-1】有关,与其他都无关,因此可以用一个变量来记住前一个的最大连续数组和就可以了。

这样就可以节省空间了。

时间复杂度:O(n)     空间复杂度:O(1)

【代码六】

    /*--------------------------------------------*   日期:2015-02-03*   作者:SJF0115*   题目: 53.Maximum Subarray*   网址:https://oj.leetcode.com/problems/maximum-subarray/*   结果:AC*   来源:LeetCode*   博客:-----------------------------------------------*/class Solution {public:int maxSubArray(int A[], int n) {int maxSum = A[0];// sum 记住前一个的最大连续数组和int sum = 0;// 动态规划for(int i = 0;i < n;i++){sum += A[i];sum = max(sum,A[i]);if(sum > maxSum){maxSum = sum;}//if}//forreturn maxSum;}};

【解法七】
时间复杂度:O(n)     空间复杂度:O(1)

/*********************************
*   日期:2015-01-27
*   作者:SJF0115
*   题目: 53.Maximum Subarray
*   网址:https://oj.leetcode.com/problems/maximum-subarray/
*   结果:AC
*   来源:LeetCode
*   博客:
**********************************/
#include <iostream>
#include <climits>
using namespace std;class Solution {
public:int maxSubArray(int A[], int n) {if(n <= 0){return 0;}//if// 最大和int max = A[0];// 当前最大和int cur = 0;for(int i = 0;i < n;++i){// 一旦当前最大和小于0就重置为0,一个负数只能使最大和变小if(cur < 0){cur = 0;}//ifcur += A[i];if(cur > max){max = cur;}//if}//forreturn max;}
};
int main(){Solution solution;int n = 9;int A[] = {-2,1,-3,4,-1,2,1,-5,4};int result = solution.maxSubArray(A,n);// 输出cout<<result<<endl;return 0;
}



【相似题目】

UVA之1121 - Subsequence

[LeetCode]53.Maximum Subarray






这篇关于编程之美之2.14 求数组的子数组之和的最大值的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/723341

相关文章

Java中的数组与集合基本用法详解

《Java中的数组与集合基本用法详解》本文介绍了Java数组和集合框架的基础知识,数组部分涵盖了一维、二维及多维数组的声明、初始化、访问与遍历方法,以及Arrays类的常用操作,对Java数组与集合相... 目录一、Java数组基础1.1 数组结构概述1.2 一维数组1.2.1 声明与初始化1.2.2 访问

MySQL查询JSON数组字段包含特定字符串的方法

《MySQL查询JSON数组字段包含特定字符串的方法》在MySQL数据库中,当某个字段存储的是JSON数组,需要查询数组中包含特定字符串的记录时传统的LIKE语句无法直接使用,下面小编就为大家介绍两种... 目录问题背景解决方案对比1. 精确匹配方案(推荐)2. 模糊匹配方案参数化查询示例使用场景建议性能优

关于集合与数组转换实现方法

《关于集合与数组转换实现方法》:本文主要介绍关于集合与数组转换实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Arrays.asList()1.1、方法作用1.2、内部实现1.3、修改元素的影响1.4、注意事项2、list.toArray()2.1、方

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

JAVA数组中五种常见排序方法整理汇总

《JAVA数组中五种常见排序方法整理汇总》本文给大家分享五种常用的Java数组排序方法整理,每种方法结合示例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录前言:法一:Arrays.sort()法二:冒泡排序法三:选择排序法四:反转排序法五:直接插入排序前言:几种常用的Java数组排序

Python 异步编程 asyncio简介及基本用法

《Python异步编程asyncio简介及基本用法》asyncio是Python的一个库,用于编写并发代码,使用协程、任务和Futures来处理I/O密集型和高延迟操作,本文给大家介绍Python... 目录1、asyncio是什么IO密集型任务特征2、怎么用1、基本用法2、关键字 async1、async

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a