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

部分正確

16 01-複雜度1 Java (openjdk) 374 ms 慕珩
測試點 提示 結果 耗時 內存
0 sample 有正負,負數開頭結尾,最大和有更新 答案正確 92 ms 10820 KB
1 100個隨機數 答案正確 145 ms 10768 KB
2 1000個隨機數 答案正確 374 ms 14424 KB
3 10000個隨機數 運行超時 -- 0 KB
4 100000個隨機數 運行超時 -- 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

答案正確

20 01-複雜度1 Java (openjdk) 5642 ms 慕珩
測試點 提示 結果 耗時 內存
0 sample 有正負,負數開頭結尾,最大和有更新 答案正確 115 ms 10920 KB
1 100個隨機數 答案正確 146 ms 10856 KB
2 1000個隨機數 答案正確 199 ms 14184 KB
3 10000個隨機數 答案正確 360 ms 23524 KB
4 100000個隨機數 答案正確 5642 ms 52788 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

答案正確

20 01-複雜度1 Java (openjdk) 667 ms 慕珩
測試點 提示 結果 耗時 內存
0 sample 有正負,負數開頭結尾,最大和有更新 答案正確 120 ms 10832 KB
1 100個隨機數 答案正確 130 ms 10704 KB
2 1000個隨機數 答案正確 169 ms 12692 KB
3 10000個隨機數 答案正確 293 ms 21396 KB
4 100000個隨機數 答案正確 667 ms 50196 KB

相比較第二個方法,確實快了不少。

2019/10/19 15:33:04

答案正確

20 01-複雜度1 Java (openjdk) 667 ms 慕珩
2019/10/19 14:54:27

答案正確

20 01-複雜度1 Java (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

答案正確

20 01-複雜度1 Java (openjdk) 543 ms 慕珩
測試點 提示 結果 耗時 內存
0 sample 有正負,負數開頭結尾,最大和有更新 答案正確 121 ms 10952 KB
1 100個隨機數 答案正確 119 ms 10796 KB
2 1000個隨機數 答案正確 129 ms 13336 KB
3 10000個隨機數 答案正確 198 ms 22740 KB
4 100000個隨機數 答案正確 543 ms 52336 KB

2、3、4方法相比:

4 543 ms
3 667 ms
2 5642 ms

 

寫在最後:因爲不太喜歡C語言(因爲指針那些的沒學好= =),所以學數據結構就想嘗試用java寫出來,不過我猜以後考研還會再用c寫(想吐)。

這個最大子列和問題雖然是對着mooc上的思路來的,但是每一步現在都很清晰明瞭了,自己寫也可以寫出來了,也算是一大突破。(捂臉,以前太懶太笨了,寫不出來。)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章