Demo003 最大连续子数组问题(《算法导论》4.1-5)
问题
问题描述
给定n个整数(可能为负数)组成的数组,要求一个数组连续下标和的最大值,数组的元素可正、可负、可为零。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。
例如输入的数组为 -2 , 11 , -4 , 13 , -5 , -2时,最大的子数组为11,-4,13,因此最大子数组的和为20。
解题思路
很容易理解,当我们加上一个正数时,和会增加;当我们加上一个负数时,和会减少。如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,不然的话这个负数将会减少接下来的和。
从其实点i到目标点j,从第一个正数开始截取尽量长的一段数组,从第一个正数起的最大子数组即为当前数组的最大子数组,若数组和为负,则不可能作为更长数组最大子数组的组成部分(因为还不如从零直接取第一个正数),因此清零数组和并从接下来的第一个正数重新截取。
在此过程中,我们通常说“一个最大子数组”而不是“最大子数组”,因为可能有多个子数组达到最大和。
只有当数组中包含负数时,最大子数组问题才有意义。如果所有数组元素都是非负的,最大子数组问题没有任何难度,因为整个数组的和肯定是最大的。
思路1:暴力枚举法
1.思路分析
先找出从第1个元素开始的最大子数组(先从第一个元素开始向后累加,每次累加后与之前的和比较,保留最大值),
而后再从第2个元素开始找出从第2个元素开始的最大子数组,依次类推,比较得出最大的子数组。
记:最大子数组的和为maxium(起点为 low ,终点为 high),初始值为-INF;
正在扫描的子数组的和为sum(起点为 i ,终点为 j ),初始值为0。

2.复杂度
遍历所有可能的sum[i, …, j],时间复杂度为O(n^2),空间复杂度O(1)。
3.伪代码
int maxSubArray(int *arr,int n,int &low,int &high)
{
int maxium = -INF; //maxium 最大子数组的和 初始值为-INF
for i=0 to n-1 do
sum = 0; //正在扫描的子数组的和 初始值为0
for j=i to n-1 do
sum += arr[j];
if sum>maxium do //将 maxium 更新为 sum
maxium = sum;
low=i;
high=j;
return maxium;
}
4.analysis in C
int maxSubArray(int *arr,int n,int &low,int &high)
{
int maxium=-INF;
for(int i=0;i<=n-1;i++){
int sum=0;
for(int j=i;j<=n-1;j++)
{
sum += arr[j];
if(sum > maxium)
{
maxium = sum;
low = i;
high = j;
}
}
}
return maxium;
}
5.analysis in java
package com.ryanjie.task03;
/**
* @program: Demo003
* @description: return MaxSubArray
* @author: Ryanjie
* @create: 2018-04-01 19:42
**/
public class MaxSubArray1{
public int Method1(int[] arr){
int maxium = arr[0];
int sum = 0;;
for(int i = 0;i < arr.length;i++){
for(int j = i;j < arr.length;j++){
for(int k = i;k <= j;k++){
sum += arr[k];
}
if(sum > maxium)
maxium = sum;
sum = 0; //完成一个子序列求和后,重新赋值为0
}
}
return maxium;
}
public static void main(String[] args){
MaxSubArray1 test = new MaxSubArray1();
int[] A = {-2 , 11 , -4 , 13 , -5 , -2};
System.out.println("蛮力法求解数组A的最大连续子数组和为:"+test.Method1(A));
}
}
测试结果:
蛮力法求解数组A的最大连续子数组和为:20
思路2:分治法
《算法导论》分治法---分而治之:想要为一个问题找出分治的解法,关键在于怎么分和分了之后怎么找出一个时间复杂度可以接受的算法 。
1.思路分析
我们把数组A[1..n]划分为两个规模尽量相等的子数组:arr[1..n/2] 和 A[n/2+1..n],也就是说,找到数组的中间位置mid(n/2).
最大的子数组A[low..high]只可能出现在三种情况(如图2所示):
- 完全位于子数组 A[ 1...mid]中 ;
- 完全位于子数组 A[ mid+1...n ]中 ;
- 跨越了中点,在子数组 A[ i..mid..j ]中 ;

前面两种情况的解法和整体的解法一样,用递归解决,主要看第三个情况。
如图3所示,任何跨越中点的子数组一定包含左半边数组的最大后缀和右半边数组的最大前缀,都由两个子数组A[i..mid]和A[mid+1..j]组成,其中 1 <= i <= mid 且 mid+1 <= j <= n 。因此,我们只需找出形如A[i..mid]和A[mid+1..j]的最大子数组,然后将其合并即可。

2.复杂度:时间复杂度O(nlogn),空间复杂度O(1)
3.伪代码
int maxSubArray(int *arr,int low,int high)
{
left_sum = -INF;
right_sum = -INF;
max_left = -1;
max_right = -1;
sum = 0; //from mid to left search left_sum
for i=mid downto low do
sum += arr[i];
if sum > left_sum do
left_sum = sum;
max_left = i;
sum = 0; //from mid to right search right_sum
for i=mid+1 to high do
sum += arr[j];
if sum > right_sum do
right_sum = sum;
max_right = j;
return (left_sum + right_sum);
}
4.analysis in C:
int maxSubArray(int *arr,int low,int right)
{
int left_sum = -INF;
int right_sum = -INF;
int max_left = -1,;
int max_right = -1;
int sum = 0; //from mid to left search left_sum
for (int i = mid; i >= low; i --) {
sum += arr[i];
if (sum > left_sum) {
left_sum = sum;
max_left = i;
}
}
sum = 0; //from mid to right search right_sum
for (int j = mid + 1; j <= high; j ++) {
sum += arr[j];
if (sum > right_sum) {
right_sum = sum;
max_right = j;
}
}
return (left_sum + right_sum);
}
思路3:动态规划
1.思路分析
令cursum(i)表示数组下标以i为起点的最大连续下标最大的和,而maxsum(i)表示前i个元素的最大子数组之和。那么我们就可以推出下一个maxsum(i+1)应该为cursum(i+1)和maxsum(i)中选取一个最大值。递推式为:
cursum(i) = max{A[i],cursum(i-1)+A[i]};
maxsum(i) = max{maxsum(i-1),cursum(i+1)};
转移方程为:
f_i=max(f_i-1+a_i,a_i)
2.复杂度:时间复杂度O(n)
3.伪代码
int maxSubArray(int *A,int n)
{
cursum = A[0];
maxsum = A[0];
for i=1 to n-1 do
/*当我们加上一个正数时,和会增加;当我们加上一个负数时,和会减少。如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,不然的话这个负数将会减少接下来的和。*/
if cursum<0 do
cursum = 0;
cursum += A[i];
if cursum>maxsum do
maxsum = cursum;
return maxsum;
}
4.analysis in java
package com.ryanjie.task03;
/**
* @program: Demo003
* @description: return MaxSubArray
* @author: Ryanjie
* @create: 2018-04-01 19:50
**/
public class MaxSubArray2 {
int Method2(int[] A){
int cursum = A[0];
int maxsum = cursum;
for(int i = 0;i < A.length;i++){
if(cursum >= 0)
cursum += A[i];
else
cursum = A[i];
if(cursum > maxsum)
maxsum = cursum;
}
return maxsum;
}
public static void main(String[] args){
MaxSubArray2 test = new MaxSubArray2();
int[] A = {-2 , 11 , -4 , 13 , -5 , -2};
System.out.println("动态规划法求解数组A的最大连续子数组和为:"+test.Method2(A));
}
}
测试结果:
动态规划法求解数组A的最大连续子数组和为:20
测试
从语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、条件组合覆盖五个覆盖标准中选取条件组合覆盖进行测试,测试代码如下:
package com.ryanjie.task03;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* MaxSubArray2 Tester.
*
* @author Ryanjie
* @since <pre>04/01/2018</pre>
* @version 1.0
*/
public class MaxSubArray2Test {
@Test
public void testManualAllPositiveArray() {
int arr[] = new int[] {1, 2, 3, 4, 5, 6};
assertEquals(22, new MaxSubArray2().Method2(arr));
}
@Test
public void testManualAllPositiveArray1() {
int[] arr = new int[]{0, 0, 18, 0, 0, -6};
assertEquals(18, new MaxSubArray2().Method2(arr));
}
@Test
public void testManualAllPositiveArray2() {
int[] arr = new int[] {-2, 11, -4, 13, -5, -2};
assertEquals(20, new MaxSubArray2().Method2(arr));
}
@Test
public void testManualAllPositiveArray3() {
int[] arr = new int[] {-2 , 11 , -4 , 13 , -5 , -2};
assertEquals(20, new MaxSubArray2().Method2(arr));
}
}
测试截图:

coding代码:Task03
Demo003 最大连续子数组问题(《算法导论》4.1-5)的更多相关文章
- php算法题---连续子数组的最大和
php算法题---连续子数组的最大和 一.总结 一句话总结: 重要:一定要本机调试过了再提交代码,因为很容易出现考虑不周的情况,或者修改了之后没有考虑修改的这部分 利用空间来换时间,或者利用空间来换算 ...
- 编程算法 - 连续子数组的最大和 代码(C)
连续子数组的最大和 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 输入一个整型数组, 数组里有正数也有负数. 数组中一个或连续的多个整数组成一 ...
- 算法笔记_043:最大连续子数组和(Java)
目录 1 问题描述 2 解决方案 2.1 蛮力枚举法 2.2 动态规划法 1 问题描述 给定一个整数数组,数组里可能有正数.负数和零.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和 ...
- 剑指offer面试题31连续子数组的最大和
一.题目描述 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决.但是,如果 ...
- 最大连续子数组问题2-homework-02
1) 一维数组最大连续子数组 如第homework-01就是一维数组的最大子数组,而当其首位相接时,只需多考虑子数组穿过相接的那个数就行了! 2)二维数组 算法应该和第一次的相似,或者说是将二维转化为 ...
- Java课程课后作业190315之最大连续子数组(二维数组版)
,, 在本周的课堂上,老师再一次提高了要求,将一维数组升级成为了二维数组,然后求出块状的连续子数组. 一开始还想着借鉴之前球一维数组的O(n)的算法,后来还是没有找到头绪,舍友讲了自己的办法,但是没有 ...
- 个人实战演练全过程——No.1 最大连续子数组求和
之前的一次个人总结和一次单元测试入门学习是开启软件工程课程的前奏曲,也是热身,现在大家对于这门课程也有了初步的了解和认识,这次要开始真正的演奏了,要从头到尾完全靠自己的能力来解决一个问题,进行实战演练 ...
- 【LeetCode】Maximum Product Subarray 求连续子数组使其乘积最大
Add Date 2014-09-23 Maximum Product Subarray Find the contiguous subarray within an array (containin ...
- 连续子数组的最大乘积及连续子数组的最大和(Java)
1. 子数组的最大和 输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.例如数组:arr[]={1, 2, 3, -2, ...
随机推荐
- IntelliJ IDEA中文乱码问题
转自 https://blog.csdn.net/m0_37893932/article/details/78280663 1 file->settings->appearence里面有 ...
- ABP-JavaScript API
一.AJAX 1,ABP采用的方式 ASP.NET Boilerplate通过用abp.ajax函数包装AJAX调用来自动执行其中的一些步骤. 一个例子ajax调用: var newPerson = ...
- 学习《Unix/Linux编程实践教程》(2):实现 more
0.目录 1.more 能做什么? 2.more 是如何实现的? 3.实现 more 3.1 more01.c 3.2 more02.c 3.3 more03.c 1.more 能做什么? more ...
- 自动化运维—Ansible(上)
一:为什么选择Ansible 相对于puppet和saltstack,ansible无需客户端,更轻量级 ansible甚至都不用启动服务,仅仅只是一个工具,可以很轻松的实现分布式扩展 更强的远程命令 ...
- Codeforces Round #426 (Div. 1) B The Bakery (线段树+dp)
B. The Bakery time limit per test 2.5 seconds memory limit per test 256 megabytes input standard inp ...
- C源程序怎么变成可执行文件
本文所有内容都是来自网上的转载,文末有转载链接. 电子计算机所使用的是由“0”和“1”组成的二进制数,二进制是计算机的语言的基础.计算机发明之初,人们只能降贵纡尊,用计算机的语言去命令计算机干这干那, ...
- UVAlive-7040 color(组合数学,二项式反演)
链接:vjudge 题目大意:有一排方格共 $n$ 个,现在有 $m$ 种颜色,要给这些方格染色,要求相邻两个格子的颜色不能相同.现在问恰好用了 $k$ 种颜色的合法方案数.答案对 $10^9+7$ ...
- java同步代码(synchronized)中使用BlockingQueue
说起BlockingQueue,大家最熟悉的就是生产者-消费者模式下的应用.但是如果在调用queue的上层代码加了同步块就会导致线程死锁. 例如: static BlockingQueue<St ...
- 51nod 1667 概率好题
Description: 甲乙进行比赛. 他们各有k1,k2个集合[Li,Ri] 每次随机从他们拥有的每个集合中都取出一个数 S1=sigma甲取出的数,S2同理 若S1>S2甲胜 若S1=S2 ...
- boost::unique_lock和boost::lock_guard的区别
lock_guard unique_lock boost::mutex mutex; boost::unique_lock<boost::mutex> lock(mutex); std:: ...