1. 程式人生 > >求解最大連續子數組和問題

求解最大連續子數組和問題

百度 遞歸 基本 既然 我們 int dfa 更多 static

前言

最近工作不是特別忙,所以有更多時間來學習算法相關知識,補短處。題目來源於leetcode,通過一個算法題,我們去分析該算法題所屬類型,以及解題思路,以及該算法題所用到的數學知識。選擇的算法題目從容易到困難,逐步提高難度,解題的思路也是從簡單到復雜,時間復雜度也是從低到高的順序來書寫這個系列的博客。因工作語言和使用熟練度原因算法采用Java編寫,但該系列中可能會穿插c、C++、python語言實現,請不要奇怪。

題目

技術分享圖片

分析題目:給定的整型數組,求解最大連續子數組和,要求是至少包含一個元素,且算法時間復雜度至少O(n).
首先我們想到就是:計算所有子數組的和進行比較取最大值,該方式叫做暴力求解,如果在數據規模很小的情況下我們就可以很輕松的接觸結果,但一旦 規模稍微大一些,其性能就及其低了。那麽我們先來寫一下暴力求解算法實現。

暴力求解算法

    // 1.暴力解析
    private static int maxSubArrayByVoilence(int[] nums) {
        if(nums.length == 1){
            return nums[0];
        }
        int currentSubSum,maxSubSum=nums[0];
        // 思路: 1.暴力破解。遍歷子數組的和進行比較
        for(int i =0; i < nums.length; i++){
            for (int j = i; j < nums.length; j++){
                // 當前子數組和
                currentSubSum = 0;
                for (int k = i; k <= j; k++){
                    currentSubSum += nums[k];
                }
                // 比較最大值與當前子數組和
                if(currentSubSum>maxSubSum){
                    maxSubSum = currentSubSum;
                }
            }
        }
        return maxSubSum;
    }

分析:該解題方法的時間復雜度為O(n^3).效率非常低,但邏輯非常簡單。針對暴力破解算法,我們可以考慮進行優化,使其時間復雜度降低為O(n^2),因為簡單所以不再這裏多寫,同時也留給讀者自行編寫,如果可以你可以留言下來給其他讀者或者給我看,看看你的優化思路。

最大連續子數組問題讓我想到了《數據結構與算法》中的一個名詞:最優解問題。該類問題就是屬於最優解類型的題目,所以我們可以通過最優解問題的解題思路來解決這個算法題目:動態規劃算法。

動態規劃算法

動態規劃算法-百度詞條
動態規劃算法的基本思想與分治法類似,也是將待求解的問題分解為若幹個子問題(階段),按順序求解子階段,前一子問題的解,為後一子問題的求解提供了有用的信息。在求解任一子問題時,列出各種可能的局部解,通過決策保留那些有可能達到最優的局部解,丟棄其他局部解。依次解決各子問題,最後一個子問題就是初始問題的解。

由於動態規劃解決的問題多數有重疊子問題這個特點,為減少重復計算,對每一個子問題只解一次,將其不同階段的不同狀態保存在一個二維數組中。

上面是百度詞條的解釋,理解起來不難,那麽我們這個問題,該如何實現算法呢?廢話不多說,代碼先擼出來。

    // 3.動態規劃
    private static int maxSubArrayByDP(int[] nums) {
        if(nums.length == 1){
            return nums[0];
        }
        int maxSubSum = nums[0],currentSubSum = nums[0];
        for(int i=1;i<nums.length;i++){
            currentSubSum += nums[i];
            // 如果加上nums[i]後反而更小了,則從nums[i]開始重新計算
            if(currentSubSum < nums[i]){
                currentSubSum = nums[i];
            }
            // 比較大小
            if(currentSubSum > maxSubSum){
                maxSubSum = currentSubSum;
            }
        }
        return maxSubSum;
    }

分析:動態規劃算法時間復雜度為O(n),相比較於暴力解法效率就大大提升了。關鍵點在於如果加上nums[i]後相比較nums[i]小,那麽說明i之前和比i數值小,所以既然比之前小,那麽就可以把前面的都拋開重新規劃計算和。
動態規劃的數學表達:currentSubSum(i)表示數組下標以i為起點的最大連續下標最大的和,而maxsum(i)表示前i個元素的最大子數組之和。那麽我們就可以推出下一個maxsum(i+1)應該為cursum(i+1)和maxsum(i)中選取一個最大值。遞推式為:
currentSubSum(i) = max{A[i],currentSubSum(i-1)+A[i]};
maxSubSum(i) = max{maxsum(i-1),currentSubSum(i+1)};

根據follow up的說明,還可以使用分治法去求解這個算法題。那麽我們接下來看看分治法如何實現這個算法。

分治算法

// 4.分治算法+遞歸
    private static int maxSubArrayByDC(int[] num, int low, int high) throws Exception {
        // 終止條件
        if(low==high)
            return num[low];
        else if(low<high)
        {
            int mid = (low+high)/2;
            // 左側最大子數組和計算
            int left = maxSubArrayByDC(num,low,mid);
            // 右側最大子數組和計算
            int right = maxSubArrayByDC(num,mid+1,high);
            // 跨越分區子數組和計算
            int sum=0,max=0,i=0,j=0,center=0;
            for(i=mid;i>=low;i--)
            {
                sum+=num[i];
                if(sum>max)
                {
                    max = sum;
                }
            }
            center+=max;
            max = -1;
            sum=0;
            for(j=mid+1;j<=high;j++)
            {
                sum+=num[j];
                if(sum>max)
                {
                    max = sum;
                }
            }
            center+=max;
            j = left>=right?left:right;
            j = j>=center?j:center;
            return j;
        }else {
            // 拋出異常
            throw new Exception("索引值錯誤,low值一定不能比high值大");
        }
    }

分析:分治法處理規模大的問題時,采用的是縮小規模,然後比較各個規模的結果,從而得出最終結果。分治法的時間復雜度為O(nlogn)這裏關鍵是理解分治思想,對於分治思想,我從網上找了一張圖片,如下,能夠很好的幫助我們理解和記住這種思維方式。
技術分享圖片

總結

該算法題雖然是很簡單的一道題目,但包含的算法思想非常優秀。該系列的文章,我會陸續的寫一些題目的解法,但不會所有LeetCode題目都寫,我選擇題目的要求是:有意思(包括多種有意思的解題算法,或者我不懂的算法)的,有技巧的,有深度的。如果你對這題有更多想說的可以在下面留言和讀者討論,我也會一起參與的喲。

求解最大連續子數組和問題