• 求最大子段和问题


    求最大子段和的问题是比较经典的算法,可以由简单的算法到复杂的算法逐步递进。


    问题描述:

    给定序列a [1],a [2],a [3] …… a [n],您的工作是计算子序列的最大和。
    例如,给定(6,-1,5, 4,-7),此序列中的最大和为6 +(-1)+ 5 + 4 = 14。
    给定(0,6,-1,1,-6,7,-5),最大和为7。


    算法一:穷举法(枚举法)

    把所有的情况都算出来,取出最大值,这种算法的时间复杂度为O(n^3),i从数组头部到尾部,j从i到数组尾部,k从i到j。

    public int MaxSum(int[] array) {
            int currentSum = 0;
            int sum = 0;
            for (int i = 0; i < array.length; i++) {
                for (int j = i; j < array.length; j++) {
                    currentSum = 0;
                    for (int k = i; k <= j; k++) {
                        currentSum = currentSum + array[k];
                    }
                    if (currentSum > sum) {
                        sum = currentSum;
                    }
                }
            }
            return sum;
        }

    如果看不懂上面的算法,可以在纸上写出来,用了三层循环遍历计算所有的结果,在上述代码中其实有好多的计算是重复的,例如:当i=0,j=3时要计算a[0]+a[1]+a[2]+a[3],当j=4时,要计算a[0]+a[1]+a[2]+a[3]+a[4],显然这个计算有一部分是多余的。第一层循环从0到array的长度-1,第二、三层循环主要负责计算子段的和,把每次计算的子段和和存到currentSum变量中,与sum进行比较(sum存储的是最终的最大子段和),如果大则进行交换。通过分析上面的代码有大量的重复计算,效率不高。

    算法二:依然是枚举法

    分析算法一发现有大量的重复计算,避免这些重复计算可以提高很多的效率,我们发现算法一中的一个循环可以去掉。

    public int MaxSum1(int[] array) {
            int currentSum = 0;
            int sum = 0;
            for (int i = 0; i < array.length; i++) {
                currentSum = 0;
                for (int j = i; j < array.length; j++) {
                    currentSum += array[j];
                    if (currentSum > sum) {
                        sum = currentSum;
                    }
                }
            }
            return sum;
        }

    虽然算法二较算法一减少了一层循环,时间复杂度可以减少到O(n^2)。不需要k,在找到j的时候就将子段和记录下来。但是依然存在重复的计算,还不是最优的。

    算法三:动态规划

    目标减少重复计算的次数,尽可能避免重复的计算过程,依然是减少循环

    public int MaxSum2(int[] array) {
            int currentSum = 0;
            int sum = 0;
            for (int i = 0; i < array.length; i++) {
                currentSum += array[i];
                if (currentSum > sum) {
                    sum = currentSum;
                }
                if (currentSum < 0) {
                    currentSum = 0;
                }
            }
            return sum;
        }

    上面的算法过程是循环遍历整个数组,接着计算每个子段的和,判断这个子段和,第一次循环如果大于0则赋值给sum,小于0不计算当前元素,接着第二次循环加上上次循环中计算出的子段和……
    下面是动态规划算法另一种实现形式:

    public int MaxSum3(int[] array) {
            int num = array.length;
            int sum = 0;
            int y = 0;
            for (int i = 0; i < num; i++) {
                if (y > 0) {
                    y += array[i];
                } else {
                    y = array[i];
                }
                if (y > sum) {
                    sum = y;
                }
            }
            return sum;
        }

    由于只有一层循环,时间复杂度降为O(n)

  • 相关阅读:
    组合模式及C++实现
    YUV422(UYVY)转RGB565源代码及其讲解.md
    会用errno,事半功倍
    可变参数宏
    camera理论基础和工作原理
    !!!??? 2.3 核心模块与应用程序的对比
    KVM与VMware的性能比较
    单片机中定时器与计数器的区别
    编译器对变量的内存分配方式
    【转载】Modelsim 与Vivado联合仿真版本对应问题
  • 原文地址:https://www.cnblogs.com/duzhentong/p/8576495.html
Copyright © 2020-2023  润新知