问题

问题描述

 给定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. Linux命令(十五) 打包或解压文件 tar

    目录 1.命令简介 2.常用参数介绍 3.实例 4.直达底部 命令简介 tar 命令用于将文件打包或解压,扩展名一般为 ".tar" ,指定特定的参数可以调用 gzip 或 bzi ...

  2. Navicat Premium和Navicat for MySQL哪个好用?

    之前在Navicat官网下载了Navicat Premium和Navicat for MySQL使用.Navicat官网产品下载地址:https://www.navicat.com.cn/produc ...

  3. PSP(3.23——3.29)以及周记录

    3.23 9:30 10:30 15 45 Android Studio 界面设计学习 A Y min 13:00 13:15 0 15 站立会议 A Y min 23:20 23:45 0 25 英 ...

  4. Luogu1641 SCOI2010生成字符串(组合数学)

    NOI2018冒泡排序的一个子问题. #include<iostream> #include<cstdio> #include<cmath> #include< ...

  5. 【题解】 [ZJOI2008] 泡泡堂(贪心/二分图/动态规划)

    懒得复制,戳我戳我 Solution: 就是有一个贪心策略:(以下假设使\(A\)队分数更高) \(First:\)比较两个分值的最小值,如果\(A\)最小分比\(B\)最小分大就直接比较两个最小的, ...

  6. 如何使用Python对Instagram进行数据分析?

     我写此文的目的在于展示以编程的方式使用Instagram的基本方法.我的方法可用于数据分析.计算机视觉以及任何你所能想到的酷炫项目中.Instagram是最大的图片分享社交媒体平台,每月活跃用户约五 ...

  7. USACO Section 2.1 The Castle 解题报告

    题目 题目描述 有一个城堡,城堡中有若干个房间,房间与房间之间用墙来进行分隔.现在我们需要统计这个城堡有多少个房间,并且还要找出最大的房间的面积是多少(一个单元格就代表一个单元面积).城堡的主人现在想 ...

  8. Libre 6010「网络流 24 题」数字梯形 (网络流,最大费用最大流)

    Libre 6010「网络流 24 题」数字梯形 (网络流,最大费用最大流) Description 给定一个由n 行数字组成的数字梯形如下图所示.梯形的第一行有m 个数字.从梯形的顶部的m 个数字开 ...

  9. CF679E Bear and Bad Powers of 42

    一段时间不写线段树标记,有些生疏了 codeforces 679e Bear and Bad Powers of 42 - CHADLZX - 博客园 关键点是:42的次幂,在long long范围内 ...

  10. centos7搭建ELK Cluster集群日志分析平台

    应用场景:ELK实际上是三个工具的集合,ElasticSearch + Logstash + Kibana,这三个工具组合形成了一套实用.易用的监控架构, 很多公司利用它来搭建可视化的海量日志分析平台 ...