问题

问题描述

 给定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. 2013长春网赛1004 hdu 4762 Cut the Cake

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4762 题意:有个蛋糕,切成m块,将n个草莓放在上面,问所有的草莓放在同一块蛋糕上面的概率是多少.2 & ...

  2. 服务端增加WCF服务全局异常处理机制

    服务端增加WCF服务全局异常处理机制,任一WCF服务或接口方式出现异常,将统一调用WCF_ExceptionHandler.ProvideFault方法,因此不需要每个方法使用try catch写法. ...

  3. Day12-navicat for sqlite 11.1.12 patch 永久使用版

    参考来源:http://www.cnblogs.com/yueyue184/p/6407963.html 深深感谢!!! 因为最近需要用这个但是网上都是注册机没有成功注册,所以就自己动手使用ollyd ...

  4. 【BZOJ4260】Codechef REBXOR (Trie树)

    [BZOJ4260]Codechef REBXOR (Trie树) 题面 BZOJ 题解 两眼题.第一眼不会做,第二眼好简单... 前缀异或和一下,拿\(Trie\)树维护求一个在这个端点以左的最大值 ...

  5. java web 验证码-数字不变形

    controller代码: import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.a ...

  6. o2o优惠券使用预测

    前沿: 这是天池的一个新人实战塞题目,原址 https://tianchi.aliyun.com/getStart/information.htm?spm=5176.100067.5678.2.e13 ...

  7. Python常用模块-时间模块(time&datetime)

    Python常用模块-时间模块(time & datetime) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.初始time模块 #!/usr/bin/env pyth ...

  8. vue-router 编程式导航

    借助vue-router的实例方法,通过编写代码来实现导航的切换: back:回退一步 forward:前进一步 go:指定前进回退步数 push:导航到不同url,向history栈添加一个新的记录 ...

  9. bzoj千题计划282:bzoj4517: [Sdoi2016]排列计数

    http://www.lydsy.com/JudgeOnline/problem.php?id=4517 组合数+错排公式 #include<cstdio> #include<ios ...

  10. dedecms在linux上安装提示没权限解决办法

    web服务器运行的用户与目录所有者用户必须不一样,比如apache运行的用户为root,那么网站目录设置的所有者就应该不能设置为root,而是设置不同于root的用户,如apache. 我们这里假设w ...