2015/10/13 算法习题:最大子列和问题

已经正式开始学习数据结构和算法,先学了网易云课堂上的浙江大学的数据结构课,是陈越和何钦铭上的,了解了什么是数据结构和算法后,学习了一些时间空间复杂度分析的技巧,结合之前马虎掌握的学习,先从简单的题目入手学习。

题目是这样的:

给定了一个n个整数组成的序列,求它各个子列中,子列和最大的值。

输入:输入n个整数组成的序列

要求输出最大子列和。

示例:

输入:

-2 11 -4 13 -5 -2

输出:

20

做出这题的难度不是很大,至少很容易可以做到暴力求解,然而暴力求解的时间复杂度是很大的。

我用Python写了暴力求解的方法:

def MaxSubSeqSum1(list):  #O(n^3)
    lenth = len(list)
    MaxSum = 0
    for i in range(lenth):
        for j in range(lenth):
            ThisSum = 0
            for count in range(i,j+1):
                ThisSum += lst[count]
            if ThisSum > MaxSum:
                MaxSum = ThisSum
    print MaxSum

暴力求解的逻辑很直接,就是按顺序将所有子列和求出来依次比较。

由于有三重嵌套循环,所以执行时间是输入序列长度规模n的三次方,时间复杂度是O(n^3)很显然,这是很差劲的一个算法,在输入量较大时就求不出结果了。

让我们分析一下这个算法执行的过程,可以很明显地看到一个改进的地方:

我们让 i 作为子列的首坐标, j 作为子列的尾坐标,依次递增。

然后 count 在 i 和 j 之间,求i j 之间所有子列的和,比较最大值。

当 i 为 0 ,j 为 0 时,求了lst[0]的值。

当 i 为 0 ,j 为 1 时,求了lst[0], lst[0]+lst[1]的值。

当 i 为 0 ,j 为 2 时,求了lst[0], lst[0]+lst[1], lst[0]+lst[1]+lst[2]的值。

...

我们很容易就发现了一个问题,我们重复计算了前面的值,也就是说,最后一个 count 的 for 循环是根本没有意义的,只是来增加了算法的时间而已。

当然,此处是故意添加的这个问题,正常情况下不会如此写这个问题。

于是,有了第二个算法,去除count的循环:

def MaxSubSeqSum2(list):  #O(n^2)
    MaxSum = 0
    lenth = len(list)
    for i in range(lenth):
        ThisSum = 0
        for j in range(i,lenth):
            ThisSum += list[j]
            if ThisSum > MaxSum:
                MaxSum = ThisSum
    return MaxSum

这个算法时间复杂度是O(n^2),是正常想到的最常规的解法。

一般一个时间复杂度是O(n^2)的问题,都会想把它改成O(nlogn)的问题。

考虑分而治之的方法是否可行,如果采用分治法,需要讲问题规模减小。

求一个序列最大子列和,是前1/2序列的最大子列和,后1/2序列最大子列和,跨中间边界的最大子列和,三个数的最大值。这样子不断分隔,自然可以使用分治法。

举例如下:

一个序列是[-1, 2, 7, -3] 这个序列的最大子列和是下面这三个数中最大的:

  [-1, 2]这个序列的最大子列和, [7, -3]这个序列的最大子列和, 经过2,7边界的序列的最大和。

  同理,[-1, 2]这个序列的最大子列和,是[-1],[2],和经过-1,2边界的序列的最大和。

将一个问题分解成一个易于解决的问题(经过边界的序列的最大和)和两个规模较小的原问题。

而经过边界的序列的最大和是很简单的,等于从中间开始向左遍历的最大序列和,以及从中间开始向右遍历的最大序列和,然后将左右最大值相加。

以下是实现代码:

def MaxSubSeqSum3(list):  #O(nlogn)
    lenth = len(list)
    def maxSum(list, left, right):
        if left == right:
            if list[left] == 0:
                return list[left]
            else:
                return 0
        else:
            center = int((left+right)/2)
            maxLeftSum = maxSum(list, left, center)
            maxRightSum = maxSum(list, center+1, right)

            maxLeftBorderSum = 0
            leftBorderSum = 0
            for i in range(center, left-1, -1):
                leftBorderSum += list[i]
                if leftBorderSum > maxLeftBorderSum:
                    maxLeftBorderSum = leftBorderSum

            maxRightBorderSum = 0
            rightBorderSum = 0
            for i in range(center+1, right+1):
                rightBorderSum += list[i]
                if rightBorderSum > maxRightBorderSum:
                    maxRightBorderSum = rightBorderSum

            return max(maxLeftSum, maxRightSum,                       maxLeftBorderSum + maxRightBorderSum)
    return maxSum(list, 0, lenth-1)    

这个算法的时间复杂度具体求解过程不在这里展开,是O(nlogn)

一般来说一个O(nlogn)的算法已经足够优秀,但是这个问题其实还有O(n)的算法,也是最快的算法了,因为必须遍历数据才能知道大小:

def MaxSubSeqSum4(list):  #O(n)
    MaxSum = 0
    lenth = len(list)
    ThisSum = 0
    for i in range(lenth):
        ThisSum += list[i]
        if ThisSum > MaxSum:
            MaxSum = ThisSum
        elif ThisSum < 0:
            ThisSum = 0
    return MaxSum

这个算法看到代码后推敲就容易理解这个思路了。不详述。

-------------------------------------------------

同时,为了测试这些算法,写了一个生成随机数表的函数和测试函数,测试各个函数的运行时间和结果:

import random
import time
def MakeIntSeq(n, low, high):
    list = []
    for i in range(n):
        list.append(random.randint(low,high))
    return list

....
def test(n, low, high):
    lst = MakeIntSeq(n, low, high)
    for fcn in [MaxSubSeqSum4,MaxSubSeqSum3,MaxSubSeqSum2]:
        start = time.clock()
        num = fcn(lst)
        end = time. clock()
        print ‘\n%r:‘ % fcn.__name__
        print ‘num :%d‘% num
        print ‘Time:‘,end - start
        

由于第一种算法能力太弱,没有测试,事实上加入它时,当n在1000左右时就要等待时间才能得到结果了。大家可以试试执行时间。

时间: 2024-10-31 00:10:17

2015/10/13 算法习题:最大子列和问题的相关文章

Murano Weekly Meeting 2015.10.13

Meeting time: 2015.October.13th 1:00~2:00 Chairperson:  Serg Melikyan, PTL from Mirantis Meeting summary: 1.Murano Networking 'max_environments' Desc:  Murano increased the number of environments per tenant in Mitaka. That means maximum number of env

2015.10.13课堂

课堂例子 ClassAndObjectTest.java 结果截图 Hellow.java源程序以及结果截图 输出结果没有什么特别 InitializeBlockDemo.java源程序以及结果截图 MyPackageClass.java源程序以及结果截图 ObjectEquals.java源程序以及结果截图 StrangeIntegerBehavior.java源程序以及结果截图 Test1.java源程序以及结果截图 字符串倒序输出 Test2.java源程序以及结果截图 调用函数的倒序输出

数据结构习题记录(1)——最大子列和问题

题目: 给定KK个整数组成的序列{ N_1N?1??, N_2N?2??, ..., N_KN?K?? },"连续子列"被定义为{ N_iN?i??, N_{i+1}N?i+1??, ..., N_jN?j?? },其中 1 \le i \le j \le K1≤i≤j≤K."最大子列和"则被定义为所有连续子列元素的和中最大者.例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20.现要求你编写程序,计

从最大子列和问题看几种不同的算法思想

题目描述如下: 只看题目描述不看测试数据特点的话,第一眼能想到的算法无非就是利用遍历逐个相加,算出每一种可能的子列和,然后返回其中最大的子列和,看看代码如何实现 int MaxSumSeq(int a[],int len){ int ThisSum=0,MaxSum=0; for(int i=0;i<len;i++){//子列的左端 for(int j=i;j<len;j++){//子列的右端,循环实际上是左端一个个加,到右端为止,然后再移动左端 ThisSum+=a[i]; if(ThisS

应用实例——最大子列和问题

题目描述:给定N个整数的序列{A1,A2,...,AN},求函数f(i,j)=max{0,ΣAk(i<=k<=j)}的最大值 算法1: 1 int MaxSubseqSum1(int A[],int N){ 2 int ThisSum,MaxSum=0; 3 int i,j,k; 4 for(i=0;i<N;i++){//i是子列左端位置 5 for(j=i;j<N;j++){//j是子列右端位置 6 ThisSum=0; 7 for(k=i;k<j;k++){//ThisS

最大子列和问题(剑指offer和PAT)

01-复杂度1 最大子列和问题   (20分) 给定KK个整数组成的序列{ N_1N?1??, N_2N?2??, ..., N_KN?K?? },“连续子列”被定义为{ N_iN?i??, N_{i+1}N?i+1??, ..., N_jN?j?? },其中 1 \le i \le j \le K1≤i≤j≤K.“最大子列和”则被定义为所有连续子列元素的和中最大者.例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20.现要求你编

复杂度_最大子列和问题(1)

给定K个整数组成的序列{ N1??, N2??, ..., NK},"连续子列"被定义为{ Ni??, Ni+1??, ..., Nj},其中 1≤i≤j≤K."最大子列和"则被定义为所有连续子列元素的和中最大者.例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20.现要求你编写程序,计算给定整数序列的最大子列和. 本题旨在测试各种不同的算法在各种数据情况下的表现.各组测试数据特点如下: 数据1:与样

代码面试最常用的10大算法

摘要:面试也是一门学问,在面试之前做好充分的准备则是成功的必须条件,而程序员在代码面试时,常会遇到编写算法的相关问题,比如排序.二叉树遍历等等. 在程序员的职业生涯中,算法亦算是一门基础课程,尤其是在面试的时候,很多公司都会让程序员编写一些算法实例,例如快速排序.二叉树查找等等. 本文总结了程序员在代码面试中最常遇到的10大算法类型,想要真正了解这些算法的原理,还需程序员们花些功夫. 1.String/Array/Matrix 在Java中,String是一个包含char数组和其它字段.方法的类

01-复杂度1. 最大子列和问题(20)

给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= K.“最大子列和”则被定义为所有连续子列元素的和中最大者.例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20.现要求你编写程序,计算给定整数序列的最大子列和. 输入格式: 输入第1行给出正整数 K (<= 100000):第2行给出K个整数,其间以空格分隔