leetcode-最大子序和

題目:

題目鏈接https://leetcode-cn.com/problems/maximum-subarray/description/


背景:

????????本題為非常經(jīng)典的一道算法入門題,有著多種非常高效的解題方法,可以幫助答題感受到“找到問(wèn)題的關(guān)鍵與解決問(wèn)題的核心最小點(diǎn)”這個(gè)思維的關(guān)鍵。

????????原本覺(jué)得此題很簡(jiǎn)單,也很容易給同事們講清楚。實(shí)際在講的時(shí)候發(fā)現(xiàn)自己也并沒(méi)有把所有的方法的根本原理徹底想清楚,所以在此做一個(gè)完整的整理與分析,供自己以后回憶、以及讓大家更好的理解。

? ? ? ? 在此給大家分享五種解法,歡迎一起討論以及使用其它方法實(shí)現(xiàn)。


一、無(wú)暴力,不解題

? ? ? ? 作為個(gè)人習(xí)慣,遇到一個(gè)問(wèn)題總是喜歡先考慮能否用暴力方法解決問(wèn)題。因?yàn)楸┝Ψ椒ǖ乃季S方式最為簡(jiǎn)單,并不需要過(guò)多的考慮問(wèn)題背后所蘊(yùn)含的原理與思路。而且在實(shí)際生活中,可能很多問(wèn)題我們使用暴力方式就足夠了,這樣會(huì)給開(kāi)發(fā)以及運(yùn)維人員都大大減少工作量。

方法一,暴力到底:

? ? ? ? 題目要求是找出最大子序和,那么我們就把所有的子序都找出來(lái),并且求出每個(gè)子序的和然后找到最大的一個(gè)就可以了。題目已給一個(gè)序列,我們需要做的就是確定所有能找到的開(kāi)始序號(hào)與結(jié)束序號(hào),然后把這個(gè)序號(hào)中的所有子序和都求出來(lái)。具體實(shí)現(xiàn)如下:

????????此方法中我們用了三層循環(huán),即在確定了某一個(gè)子序的開(kāi)始序號(hào)(i)與結(jié)束序號(hào)(j)的位置后,枚舉i與j中間的每個(gè)點(diǎn)k,計(jì)算從i到k的子序和,時(shí)間復(fù)雜度是n^3,n為序列的長(zhǎng)度。

方法2,暴力也可以優(yōu)化一點(diǎn):

? ? ? ? 在方法一中,對(duì)于i與j中間的每一個(gè)點(diǎn)k,我們都會(huì)計(jì)算一遍從i到k的所有數(shù)之和,其實(shí)這里我們做了很多次無(wú)用的計(jì)算,即點(diǎn)i到點(diǎn)k的數(shù)之和,為點(diǎn)i到點(diǎn)k-1的數(shù)之和,加上點(diǎn)k的數(shù)值。同時(shí),我們每一個(gè)起點(diǎn)i開(kāi)始的子序列,最長(zhǎng)的結(jié)尾j都一定是在完整序列的最后,所以這里我們只需要兩層循環(huán)就可以了:

方法3,直接暴力但是可靠的線性算法:

????????在講這個(gè)算法前,首先我們需要弄清楚的是最大子序和的這個(gè)子序所包含的一些特性:

*1).最大子序的開(kāi)始和結(jié)尾兩個(gè)數(shù)一定都是正數(shù),如果不是,那么子序列拋棄這個(gè)負(fù)數(shù),會(huì)變的更大。

*2).最大子序列中任何一個(gè)包含了第一個(gè)數(shù)的左子序列或者包含了最后一個(gè)數(shù)的右子序列,其和一定是正數(shù),如果不是,那么最大子序列拋棄這一段子序,會(huì)變得更大。

*3).最大子序列外左邊第一個(gè)數(shù)開(kāi)始往左任意連續(xù)多個(gè)數(shù)之和以及最大子序列外右邊第一個(gè)數(shù)開(kāi)始任意多個(gè)數(shù)之和,一定都是小于零的,否則最大子序列可以加上這一段數(shù)變得更大。

????????由以上三個(gè)特性,我們可以實(shí)現(xiàn)這樣一個(gè)方法:

? ? ? ? 我們從序列的第一個(gè)數(shù)開(kāi)始往后掃描。如果這個(gè)數(shù)小于零,那么它一定不會(huì)是最大子序列的第一個(gè)數(shù),我們就繼續(xù)往后掃描(特性*1)。如果這個(gè)數(shù)大于零,我們認(rèn)為它可能是最大子序列的第一個(gè)數(shù),我們從這個(gè)數(shù)開(kāi)始求和,每往后掃到一個(gè)數(shù),就把新的數(shù)加到這個(gè)和,只要這個(gè)和還大于零,這連續(xù)的子序列就可能是最大子序列的一部分(特性*2)。在不斷加數(shù)的過(guò)程中,我們維護(hù)一個(gè)全局的變量,用來(lái)記錄最大子序和,每次掃描一個(gè)數(shù),都判斷這個(gè)變量能否更新。一旦我們?cè)诩拥倪^(guò)程中,子序和小于零了,那么這之前的所有數(shù)我們都直接拋棄,因?yàn)橹暗臄?shù)不會(huì)是最大子序列的一部分(特性*3),我們從下一個(gè)數(shù)開(kāi)始計(jì)算新的子序列。

? ? ? ? 如此操作,是需要我們對(duì)整個(gè)序列從左到右掃描一次,我們就可以在這個(gè)過(guò)程中得到最大子序和。實(shí)現(xiàn)如下:

時(shí)間復(fù)雜度O(n)

方法4,狀態(tài)轉(zhuǎn)移,動(dòng)態(tài)規(guī)劃:

? ? ? ? 方法3中,可以認(rèn)為我們每次都是確定了一個(gè)子序列的第一個(gè)數(shù),然后從該數(shù)開(kāi)始計(jì)算之后的子序列,其實(shí)我們也可以根據(jù)子序列的最后一個(gè)數(shù)來(lái)判斷子序列的和的大小。

? ? ? ? 最大子序列一定是以某一個(gè)序列中的數(shù)為結(jié)尾,這個(gè)數(shù)一定是正數(shù)。對(duì)序列中的每個(gè)數(shù),都有兩種可能:

(1)這個(gè)數(shù)與之前若干數(shù)組成序列,這個(gè)數(shù)是最后一個(gè)。

(2)這個(gè)數(shù)就是單獨(dú)的一個(gè)序列。

? ? ? ? 我們認(rèn)為,在任意一個(gè)位置i,如果確認(rèn)了i位置上的數(shù)是序列的最后一個(gè)數(shù),那么這個(gè)序列的和最大應(yīng)該是以i-1為結(jié)尾的序列加上i位置的數(shù),和單獨(dú)只有i這個(gè)位置上的數(shù),兩者中較大的一個(gè)。如果以f[i]記錄以第i個(gè)位置的數(shù)為結(jié)尾的最大子序之和,具體狀態(tài)轉(zhuǎn)移如下:

????????f[i] = max( f[i-1] + nums[i],? nums[i])

????????如此我們掃描一遍整個(gè)數(shù)組,從第一個(gè)位置到最后一個(gè)位置,每次都已當(dāng)前位置為子序列最后一個(gè)數(shù),求得當(dāng)前位置結(jié)束的最大子序列,同時(shí)更新全局的最大子序和,便能得到最終的最大子序和。具體實(shí)現(xiàn)如下:

時(shí)間復(fù)雜度O(n)

方法5,最優(yōu)美的分治算法:

? ? ? ? 最大子序列只可能有三種情況:在序列的左半部分,在序列的右半部分,橫跨序列中間(包含左半部分最后一個(gè)與右半部分第一個(gè))。所以我們需要做的就是,對(duì)比著三部分的大小,然后返回最大的一個(gè)和。

? ? ? ? 對(duì)于中間部分,求和的辦法是從中間分別求出往左的最大值與往右的最大值,兩邊加起來(lái)即為最大的橫跨兩邊的子序列。

時(shí)間復(fù)雜度nlogn


? ? ? ? 解題絕對(duì)不是把題做出來(lái)、能得到一個(gè)答案就可以。嘗試不同方法的過(guò)程,其實(shí)是一個(gè)探索問(wèn)題的根本問(wèn)題的過(guò)程。找到了最根本的核心點(diǎn),也就能相應(yīng)的得到正確的解法。

? ? ? ? 使用更快的算法來(lái)解題,不僅僅是為了炫技,更是為了能解決更艱難情況下的問(wèn)題,讓所有問(wèn)題都變成可解的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 題目 解析 參考leetcode-最大子序和(四種) 第一種解法——暴力枚舉法 O(N^3) 從左往右依次找出所有...
    雇個(gè)城管打天下閱讀 448評(píng)論 0 0
  • 給定一個(gè)整數(shù)數(shù)組 nums ,找到一個(gè)具有最大和的連續(xù)子數(shù)組(子數(shù)組最少包含一個(gè)元素),返回其最大和。 示例: 輸...
    小白學(xué)編程閱讀 202評(píng)論 0 0
  • 2018年6月2日 六一就在孩子們的快樂(lè),爸爸媽媽們的疲倦中度過(guò) 我們的選址,惠州十里銀灘; 我們的休息地 (以實(shí)...
    晴婕栩閱讀 255評(píng)論 0 0
  • UI架構(gòu)小史3(MVC/MVP/MVVM)
    gadfly_only閱讀 670評(píng)論 0 51
  • 1、內(nèi)存管理 - 棧 or 堆 無(wú)論是java還是C,內(nèi)存分配,本質(zhì)上就是 棧和堆兩個(gè)類型。簡(jiǎn)單來(lái)說(shuō),代碼邏輯處理...
    軒居晨風(fēng)閱讀 519評(píng)論 0 1

友情鏈接更多精彩內(nèi)容