zoukankan      html  css  js  c++  java
  • 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 '
    %r:' % fcn.__name__
            print 'num :%d'% num
            print 'Time:',end - start
            

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

  • 相关阅读:
    November 13th 2016 Week 47th Sunday The 1st Day
    November 12th 2016 Week 46th Saturday
    November 11th 2016 Week 46th Friday
    November 10th 2016 Week 46th Thursday
    November 9th 2016 Week 46th Wednesday
    November 8th 2016 Week 46th Tuesday
    windows 7文件共享方法
    Win7无线网络共享设置方法
    常量指针和指针常量
    如何查找局域网的外网ip
  • 原文地址:https://www.cnblogs.com/SRL-Southern/p/4875248.html
Copyright © 2011-2022 走看看