【编程珠玑-读书笔记】算法设计技术—以求解"最大连续子序列和"为例

在一些情况下,算法的设计对程序的性能起着很大的作用,因此在有些时候,我们不得不多花一些时间在算法的设计上。

当然,算法的设计不是一言两语能讲清楚的,本章节作者通过对一个小问题进行研究,提出了4种不同的算法,为我们展示了在算法设计中的一些技术。

问题描述

input:一个具有n个浮点数字的序列x;

output:最大连续子序列和;

方案1:暴力遍历

看到这个问题,我想最最简单的思路就是遍历了!

maxsofar = 0
for i = [0, n)
    for j = [i, n)
        sum = 0
        //计算子序列x[i...j]的和
        for k = [i, j]
            sum += x[k]
        maxsofar = max(maxsofar, sum)

这个算法的时间复杂度为O(n^3)。

方案2

方案1可谓是不假思索得到的,那么只要我们稍加思索一下,就会发现,其中有着大量的重复计算!事实上,x[i…j]与x[i…j-1]密切相关,我们可以通过这一点来加速我们的计算。

maxsofar = 0
for i = [0, n)
    sum = 0
    for j = [i, n)
        sum += x[j]
        //此处,本次循环的sum仍可用在下一次循环上,通过累加减少运算
        maxsofar = max(maxsofar, sum)

这个算法的时间复杂度为O(n^2),比方案1好了一些。

在编程的时候,我曾经想过一个方案:

对于i = 0,…,n-1,计算出x[0…i]的总和,我们记为presum[i],于是对于x[i…j]的总和,可以通过presum[j]-presum[i-1]来计算。

于是有以下算法:

presum[-1] = 0
for i =[0,n)
    presum[i] = presum[i-1] + x[i]
maxsofar = 0
for i =[0,n)
    for j =[i,n)
        sum = presum[j] - presum[i-1]
        maxsofar = max(maxsofar, sum)

这种思路是挺好的,不过遗憾的是,在本问题中,他的复杂度为O(n^2),并没有实际性的改善。

方案3

作者在文中提出了第三种方案:分治算法。

将序列划分为两个子序列,假设第一部分的最大连续子序列和为m1,第二部分的最大连续子序列和为m2,那么答案很有可能就是m1和m2两者之一。不过,还有一种可能:答案跨越了两个部分,于是我们假设跨越了两部分的最大值为m3,答案必定在m1,m2,m3之中了。其中,对于m1,m2的计算,我们可以递归地进行。对于m3的计算,它必定包含从边界开始往左边累加得到的最大连续序列,也包含从边界开始往右边累加得到的最大连续序列。

这个方案的时间复杂度是O(n log n)。

方案4

作者所说的“扫描算法”,其实就是我们常用的动态规划。

定义b[j]为数组中包含x[j]的最大连续子序列和。

注意,b[j] 并不是1-j中最大的连续子序列的和,而是包含x[j]的最大子序列的和。

而我们所要求的是求出b[j]中最大的值。

状态方程为: b[j] = max(b[j-1] + x[j] , x[j])

//由于对于b数组,b[j-1]只在计算b[j]时用过一次而已,所以我们可以只用一个变量maxendinghere来表示!
maxsofar = 0
maxendinghere = 0
for i = [0,n)
    manendinghere = max(maxendinghere + x[i], x[i])
    mansofar = max(maxsofar, maxendinghere)

至此,算法已经优化到O(n)了!

感言

相信本章中提到的这个问题,大家并不陌生,看到这个的时候,我是觉得特别亲切的!刚接触编程的时候,用的就是方案1,后来由于OJ上题目的时间限定,问题规模扩大之后便超时了,于是也经历了方案2!而对于方案3,由于跨越分界的特殊情况,所以一时没想到~而方案4,这是在学习动态规划的时候碰到的,当时一开始还不理解,现在回头看,原来如此简单!当然,这种简单,事实上并不简单~它常常需要我们在动手编程前深思熟虑。

本例子虽小,但是却可以看到我们常用的一些算法设计技术:

记忆化搜索:算法2和4对状态进行了保存,避免了重复计算。

预处理:算法2b。

分治:算法3。

动态规划:我如何将x[0…i-1]的解决方案扩展到x[0…i],或者换句话说,假设x[0…i-1]已经解决了,如何得到x[0…i]的解决方案。

累积:算法2b。

你可能感兴趣的:(算法,编程珠玑,设计)