问题

问题描述

 给定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)的更多相关文章

  1. php算法题---连续子数组的最大和

    php算法题---连续子数组的最大和 一.总结 一句话总结: 重要:一定要本机调试过了再提交代码,因为很容易出现考虑不周的情况,或者修改了之后没有考虑修改的这部分 利用空间来换时间,或者利用空间来换算 ...

  2. 编程算法 - 连续子数组的最大和 代码(C)

    连续子数组的最大和 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 输入一个整型数组, 数组里有正数也有负数. 数组中一个或连续的多个整数组成一 ...

  3. 算法笔记_043:最大连续子数组和(Java)

    目录 1 问题描述 2 解决方案 2.1 蛮力枚举法 2.2 动态规划法   1 问题描述 给定一个整数数组,数组里可能有正数.负数和零.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和 ...

  4. 剑指offer面试题31连续子数组的最大和

    一.题目描述 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决.但是,如果 ...

  5. 最大连续子数组问题2-homework-02

    1) 一维数组最大连续子数组 如第homework-01就是一维数组的最大子数组,而当其首位相接时,只需多考虑子数组穿过相接的那个数就行了! 2)二维数组 算法应该和第一次的相似,或者说是将二维转化为 ...

  6. Java课程课后作业190315之最大连续子数组(二维数组版)

    ,, 在本周的课堂上,老师再一次提高了要求,将一维数组升级成为了二维数组,然后求出块状的连续子数组. 一开始还想着借鉴之前球一维数组的O(n)的算法,后来还是没有找到头绪,舍友讲了自己的办法,但是没有 ...

  7. 个人实战演练全过程——No.1 最大连续子数组求和

    之前的一次个人总结和一次单元测试入门学习是开启软件工程课程的前奏曲,也是热身,现在大家对于这门课程也有了初步的了解和认识,这次要开始真正的演奏了,要从头到尾完全靠自己的能力来解决一个问题,进行实战演练 ...

  8. 【LeetCode】Maximum Product Subarray 求连续子数组使其乘积最大

    Add Date 2014-09-23 Maximum Product Subarray Find the contiguous subarray within an array (containin ...

  9. 连续子数组的最大乘积及连续子数组的最大和(Java)

    1. 子数组的最大和 输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.例如数组:arr[]={1, 2, 3, -2, ...

随机推荐

  1. meta-inf文件夹以及MANIFEST.MF文件的作用

    meta-inf相当于一个信息包,目录中的文件和目录获得Java 2平台的认可与解释,用来配置应用程序.扩展程序.类加载器和服务 manifest.mf文件,在用jar打包时自动生成的. META-I ...

  2. 统计nginx日志的状态码

    日志格式 61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ &qu ...

  3. Ubutnu linux 下升级python版本,以2.x升级到3.x为例

    Linux操作系统一般 都会自带python,但是python版本会比主流低,故升级python, 主要思路:自带的python的链接link文件是在/usr/bin 下,采用sudo apt-get ...

  4. 两种常用文件分享方式 - 网络硬盘快速分享, 点对点的文件共享 BitTorrent Sync

    普通的用户经常通过电子邮件.QQ传递等方式进行文件的分享,但是由于不同的网络环境有的时候可能会有不同的限制,所以我们就需要寻找其他的方式来替代.今天就为大家推荐两个既常用又与众不同的分享方式. 中国论 ...

  5. MT【95】由参数前系数凑配系数题2

    提示:都是看$a,b$前的系数做的$a=4/3,b=2/3;a+b=\le2$,一样的可以求得$a+b$的最小值-1,当$b=\frac{1}{3},a=\frac{-4}{3}$时取到等号.此题是清 ...

  6. 详解掩膜mask

    什么是掩膜(mask) 数字图像处理中的掩膜的概念是借鉴于PCB制版的过程,在半导体制造中,许多芯片工艺步骤采用光刻技术,用于这些步骤的图形“底片”称为掩膜(也称作“掩模”),其作用是:在硅片上选定的 ...

  7. linux 系统文件类型、系统安装时间、系统启动时间、系统运行时间、设置及显示时间、系统时间和硬件时间

    系统文件类型: 1) $mout 2) df -l:仅列出本地文件系统:-h (--human-readable):-T:文件系统类型 $df -lhf 3) file -s (--special-f ...

  8. java回顾(项目前期的基本准备)

    一.     基础回顾 1   集合 1.1 集合的类型与各自的特性 ---|Collection: 单列集合 ---|List: 有存储顺序, 可重复 ---|ArrayList:  数组实现, 查 ...

  9. PHP-PSR-[0-4]代码规范

    PHP-FIG 在说啥是PSR-[0-4]规范的之前,我觉得我们有必要说下它的发明者和规范者:PHP-FIG,它的网站是:www.php-fig.org.就是这个联盟组织发明和创造了PSR-[0-4] ...

  10. centos安装lrzsz

    yum -y install lrzsz 使用rz打开上传框