zoukankan      html  css  js  c++  java
  • 最大连续子数组和算法(动态规划解释)

    之前在其他博客看到了,但是算法的关键部分完全看不懂为什么要这么做,直到最近上算法课,才终于知道到底怎么来的。

    问题描述:

      给出一个数组,求其最大连续子数组和

      例:数组{1,2,3,4,-5,10,-1,-1}的最大连续子数组和是子数组{1,2,3,4,-5,10}的和15

    算法过程:

      这个算法能从零直接想出来的人是真的厉害,我并不可以,所以我直接描述一下这个算法是怎么算的,而不描述怎么想到的了

      首先我们把原来数组记做a,然后最关键的一步,我们需要一个等长的数组b,b[j]的含义是以下一系列和中的最大值

    a[0] + a[1] + a[2] + a[3] + … + a[j]
           a[1] + a[2] + a[3] + … + a[j]
                  a[2] + a[3] + … + a[j]
                         a[3] + … + a[j]
                                ……
                                    a[j]

    那么显然可以知道的是,b数组中的最大值,就是我们想要的a的最大连续子数组和。

    接下来就是要得到数组b,初始化的话,b[0]应该为0和a[0]之中的较大者,下面探讨b[j]和b[j+1]之间的关系:

      b[j+1]只有两种取法,第一种是b[j]+a[j+1],注意b[j]本身就是最大值,所以不用再去考虑b[j]内部的情况了,第二种是a[j+1],所以

      b[j+1] = max{ b[j]+a[j+1], a[j+1] }

    OK,到此这个算法其实就算完成了,下面是直接根据这个思路打的代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int a[] = {1,2,3,4,-5,10,-1,-1};  // 原数组
        int* b = (int*)malloc(sizeof(a)); // 数组b,和原数组等长
    
        // 初始化
        b[0]= a[0] > 0 ? a[0] : 0;
    
        // 遍历构造,注意i的边界
        int length = sizeof(a) / sizeof(int); // 数组长
        for (int i = 0; i < length - 1; i++) {
            b[i+1] = b[i] + a[i+1] > a[i+1] ? b[i] + a[i+1] : a[i+1];
        }
    
        // 然后找出b中的最大值
        int max = b[0];
        for (int i = 1; i < length; i++)
            if (b[i] > max)
                max = b[i];
    
        printf("max=%i
    ", max);
        return 0;
    }
    

      然后导致我一直看不懂的地方来了,对上面这段代码进行编程上的优化,优化主要有两个地方,

      首先在上面的分析中,b[n+1]只和当前遍历到的a[n+1]和前一个b[n]直接相关,所以其实并不需要保存整个数组b,换用一个变量就可以了,其次是最大值,也是可以一遍遍历一边改的

      于是就有了目前网络上流传最多的以下这种代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int a[] = {1,2,3,4,-5,10,-1,-1};  // 原数组
        int max = 0;
        int b = 0;
    
        int length = sizeof(a) / sizeof(int); // 长度
        for (int i = 0; i < length; i++) {
            b = b + a[i] > a[i] ? b + a[i] : a[i];
            max = max > b ? max : b;
        }
    
        printf("max=%i
    ", max);
        return 0;
    }

    显然这个算法是o(n)时间复杂度的,讲真最近学动态规划基本o(n^3)起步。动态规划两个基本要素,首先最优子结构性质,全局最大值基于n个子问题b的最优解,而且可以看到b[j+1]是基于b[j]给出的,也就是一个问题是基于他的子问题的最优解来解决的,其次显然子问题是有交叉的,求解b[j]需要b[j-1],递归一下就需要b[j-2],由此就出现了交叉。

    PS:LeetCode上题号53 最大子序和就是这个问题,但是貌似子数组长度不为0,所以初始化会有一点变化。

  • 相关阅读:
    3.2 Program Encodings 程序编码
    Describe your home
    Building vs solution in command line
    找到适合自己的人生轨迹 Angkor:
    每个月总有那么几天不想学习,不想写代码 Angkor:
    Linux下的Memcache安装
    敏捷开发之 12条敏捷原则
    为什么要用NIO
    memcached server LRU 深入分析
    Linux 脚本编写基础
  • 原文地址:https://www.cnblogs.com/guobaoxu/p/11749943.html
Copyright © 2011-2022 走看看