PTA最大子列和问题(Java)

前言:这个问题是我在学习浙大数据结构mooc上看到的,所以在此总结一下。

PTA最大子列和问题:

问题描述:给定N个整数的序列{A1,A2,A3,……,An},求解子列和中最大的值。

例如给出{-2,11,-4,13,-5,-2}这样一个序列,正确的最大子列和为20

第一种方法:三层循环

原理:就是穷举,按照顺序一个一个算,很麻烦,但是很好懂。

时间复杂度:O(n^3)

import java.util.Scanner;

//最大子列和第一种方法
public class Main {
    public static int maxSubseqSum1(int a[],int N){
        int maxSum = 0;
        for (int i = 0; i < N; i++) {
            for (int j = i; j <N ; j++) {
                int thisSum = 0;
                for (int k = i; k <= j; k++) {
                    thisSum += a[k];
                }
                if(thisSum>maxSum){
                    maxSum = thisSum;
                }
            }
        }
        return maxSum;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        int a[] = new int[N];
        for (int i = 0; i < N; i++) {
            a[i] = scanner.nextInt();
        }
        System.out.println(maxSubseqSum1(a,N));
    }
}

结果果不其然,后面计算量大以后超时了,太暴力了。

提交时间状态分数题目编译器耗时用户
2019/10/19 14:40:44

部分正确

1601-复杂度1Java (openjdk)374 ms慕珩
测试点提示结果耗时内存
0sample 有正负,负数开头结尾,最大和有更新答案正确92 ms10820 KB
1100个随机数答案正确145 ms10768 KB
21000个随机数答案正确374 ms14424 KB
310000个随机数运行超时--0 KB
4100000个随机数运行超时--0 KB

第二种方法:减少一次循环

原理:相同的i不同的j,计算和的时候可以不从头开始加,直接按照刚才的那个加下一个数即可。

时间复杂度:O(n^2)

import java.util.Scanner;

//最大子列和第二种方法
public class Main {
    public static int maxSubseqSum1(int a[],int N){
        int maxSum = 0;
        //i是左端,j是右端
        for (int i = 0; i < N; i++) {
            int thisSum = 0;
            //对于相同的i,不同的j,下一次直接加在上一次的结果上。
            for (int j = i; j <N ; j++) {
                thisSum += a[j];
                if(thisSum>maxSum){
                    maxSum = thisSum;
                }
            }
        }
        return maxSum;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        int a[] = new int[N];
        for (int i = 0; i < N; i++) {
            a[i] = scanner.nextInt();
        }
        System.out.println(maxSubseqSum1(a,N));
    }
}

这一次结果正确了,没有超时。

提交时间状态分数题目编译器耗时用户
2019/10/19 14:54:27

答案正确

2001-复杂度1Java (openjdk)5642 ms慕珩
测试点提示结果耗时内存
0sample 有正负,负数开头结尾,最大和有更新答案正确115 ms10920 KB
1100个随机数答案正确146 ms10856 KB
21000个随机数答案正确199 ms14184 KB
310000个随机数答案正确360 ms23524 KB
4100000个随机数答案正确5642 ms52788 KB

第三种方法:分而治之

原理:分成左右两个部分,求两边的最大和,然后求出跨越两边的最大和,一直递归。

时间复杂度:O(NlogN)

import java.util.Scanner;

//最大子列和第三种方法
//代码是根据mooc上写出来的,有一部分稍作改动
public class Main {
    //三个数字里找最大
    public static int foundMax(int a,int b,int c){
        int max = 0;
        if(a>max){
            max = a;
        }
        if(b>max){
            max = b;
        }
        if(c>max){
            max = c;
        }
        return max;
    }
    public static int divideAndConquer(int a[],int left,int right){
        int maxSum = 0;
        //定义左边最大和,右边最大和,跨越两边扫描的左边最大和,右边最大和
        int rightMaxSum ,leftMaxSum ;
        int maxLeftBorderSum, maxRightBorderSum;
        int maxBorderSum;
        //当前左边和,当前右边和
        int leftBorderSum =0 , rightBorderSum = 0;
        //中心位置和数字
        int center, i;

        //只有一个数字的时候,最大子列和就是这个数
        // 也是递归的终止条件
        if(left==right){
            if(a[left]>0)return a[left];
            else{return 0;}
        }
        //分
        center = (right+left)/2;
        //递归得到左边最大和和右边最大和
        leftMaxSum = divideAndConquer(a,left,center);
        rightMaxSum = divideAndConquer(a,center+1,right);

        //跨中点的最大子列和
        maxLeftBorderSum = 0;
        maxRightBorderSum = 0;
        //从中点向左扫描
        for (i = center; i>=left; i--) {
            leftBorderSum += a[i];
            if(leftBorderSum>maxLeftBorderSum){
                maxLeftBorderSum = leftBorderSum;
            }
        }
        //从中点向右扫描
        for (i =center+1; i<=right ; i++) {
            rightBorderSum += a[i];
            if(rightBorderSum>maxRightBorderSum){
                maxRightBorderSum = rightBorderSum;
            }
        }
        maxBorderSum = maxLeftBorderSum+maxRightBorderSum;
        maxSum = foundMax(maxBorderSum,leftMaxSum,rightMaxSum);
        return maxSum;
    }

    public static int maxSubseqSum3(int a[],int N){
        return divideAndConquer(a,0,N-1);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        int a[] = new int[N];
        for (int i = 0; i < N; i++) {
            a[i] = scanner.nextInt();
        }
        System.out.println(maxSubseqSum3(a,N));
    }
}

结果耗时更短。

提交时间状态分数题目编译器耗时用户
2019/10/19 15:33:04

答案正确

2001-复杂度1Java (openjdk)667 ms慕珩
测试点提示结果耗时内存
0sample 有正负,负数开头结尾,最大和有更新答案正确120 ms10832 KB
1100个随机数答案正确130 ms10704 KB
21000个随机数答案正确169 ms12692 KB
310000个随机数答案正确293 ms21396 KB
4100000个随机数答案正确667 ms50196 KB

相比较第二个方法,确实快了不少。

2019/10/19 15:33:04

答案正确

2001-复杂度1Java (openjdk)667 ms慕珩
2019/10/19 14:54:27

答案正确

2001-复杂度1Java (openjdk)5642 ms慕珩

第四种方法:在线处理

原理:从第一个数开始,向右累加,如果有更大的则更新当前结果,如果变成负数则舍弃前面的,从下一个开始。

(因为负数不可能使后面的子列和增加。)

时间复杂度:O(N) (应该是最快的了,因为你总要把所有的数字扫描一遍吧)

import java.util.Scanner;

//最大子列和第4种方法
//代码是根据mooc上写出来的,有一部分稍作改动
public class Main {
    public static int onlineFound(int a[],int N){
        int thisSum =0,maxSum = 0;
        for (int i = 0; i < N; i++) {
            thisSum += a[i];
            if(thisSum>maxSum){
                maxSum = thisSum;
            }
            else if(thisSum<0){
                thisSum = 0;
            }
        }
        return maxSum;
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        int a[] = new int[N];
        for (int i = 0; i < N; i++) {
            a[i] = scanner.nextInt();
        }
        System.out.println(onlineFound(a,N));
    }
}

结果又比分而治之更快。

提交时间状态分数题目编译器耗时用户
2019/10/19 15:40:52

答案正确

2001-复杂度1Java (openjdk)543 ms慕珩
测试点提示结果耗时内存
0sample 有正负,负数开头结尾,最大和有更新答案正确121 ms10952 KB
1100个随机数答案正确119 ms10796 KB
21000个随机数答案正确129 ms13336 KB
310000个随机数答案正确198 ms22740 KB
4100000个随机数答案正确543 ms52336 KB

2、3、4方法相比:

4543 ms
3667 ms
25642 ms

 

写在最后:因为不太喜欢C语言(因为指针那些的没学好= =),所以学数据结构就想尝试用java写出来,不过我猜以后考研还会再用c写(想吐)。

这个最大子列和问题虽然是对着mooc上的思路来的,但是每一步现在都很清晰明了了,自己写也可以写出来了,也算是一大突破。(捂脸,以前太懒太笨了,写不出来。)

另外,吼一嗓子:浙大的mooc课真的很不错!!!(眼神暗示)

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值