问题

问题描述

 给定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. Centos7 安装netcat

    1.下载 下载地址:https://sourceforge.net/projects/netcat/files/netcat/0.7.1/ 下载的是netcat-0.7.1.tar.gz版本 2.安装 ...

  2. git learn

    $ git config --global user.name "Your Name" $ git config --global user.email "email@e ...

  3. Linux用户管理机制

    Linux系统中的用户管理涉及用户账号文件 /etc/passwd.用户密码文件 /etc/shadow.用户组文件 /etc/group. 一.用户账号文件 /etc/passwd 该文件为纯文本文 ...

  4. sql 索引 sql_safe_updates

    为了数据的安全性,mysql有一个安全性设置,sql_safe_updates ,当把这个值设置成1的时候,当程序要对数据进行修改删除操作的时候条件必须要走索引. 刚好现在也碰到了此类问题:网上找了相 ...

  5. asp.net使用动态模版导出word

    具体思路: 1.先制作Word模版,使用文本框+书签的方式来设计模版: 2.模版制作完之后,根据模版生成新文件,使用File.Copy方法,生成.doc格式新文件: 3.后台取得数据,参照网页渲染的方 ...

  6. windows版本SourceTree免登陆初始设置

    xi 新版本的sourceTree安装之后需要使用账户登陆授权以后才可以使用,之前的旧版本可以不能登录直接使用. sourceTree免登陆跳过初始设置方法 1.安装完sourceTree以后先运行一 ...

  7. 五、spring boot 1.5.4 集成 jpa+hibernate+jdbcTemplate

    1.pom添加依赖 <!-- spring data jpa,会注入tomcat jdbc pool/hibernate等 --> <dependency> <group ...

  8. 解题:NOIP 2018 保卫王国

    题面 最小支配集=全集-最大独立集 所以先把点权改成正无穷/负无穷来保证强制选/不选某个点到独立集里,然后变成了洛谷的动态DP模板 GTMDNOIP2018ZTY #include<stack& ...

  9. 关于xmlhttp会使用ie的缓存的问题及解决

    在浏览器(如:IE)的客户端使用xmlhttp读取网络资源的时候,需要考虑到浏览器本地缓存的问题. 如果希望读取的数据是实时更新的,也就是不想从本地缓存中读取数据,我之前常用的方法是在请求网址后面加一 ...

  10. dwr框架介绍

    转: [DWR框架]过时了吗? 置顶 2018年06月02日 11:59:02 许你笑颜 阅读数:4129   版权声明: https://blog.csdn.net/smileyan9/articl ...