最大子序列和問題的解

前言:此問題是著名的《數據結構與算法分析》第二章算法導論的第一個例子,用來闡釋算法性能有優劣之分。當輸入量的規模在很小的量級,只要問題能得到解決便已達到目的,伴隨解決問題所花費的時間空間顯得微乎其微。而當輸入量的規模極其龐大,成百上千萬時,算法性能的好壞便顯得尤爲重要,在問題解決的同時,壞算法花費幾個月幾年的時間,並且花費了電腦所有的內存,好算法在幾秒內解決問題。這就是算法性能優劣的重要性的體現。在下面的例子中舉出四種解法,所花費的時間由長到短,可以直觀的展示好壞算法的優劣之處。

問題描述:給定整數A1,A2,...,AN(可能有負數),求的最大值(爲方便期間,如果所有整數均爲負數,則最大子序列和爲0)。

例:輸入-2,11,-4,13,-5,-2,答案爲20(從A2到A4)。


解法1:枚舉法O(n3)

當沒有思路時,邏輯最簡單也是最花時間的方法就是枚舉法。解體關鍵是“最大”和“子序列”,“最大”意味着列舉所有的序列情況依次和人爲設置的MaxSum比較,比它大則將值賦給MaxSum,最終得到的MaxSum就是所有情況中最大的。“子序列”是給定數組中x個連續的數組成的序列,x取值範圍從1到N。解法一,使用了三層for循環來枚舉,則時間複雜度爲O(n3),i,j爲子序列的左右界,ThisSum是當前子序列的和。ThisSum=A[i]+...+A[j]。k是爲了從i加到j的一個局部變量。


解法二:枚舉法改良版O(n2)


枚舉法簡單在於解題者不用多想以最粗暴的方法解題,而花時間在於進行了大量的重複計算,因此不是一個優秀的算法。我們發現解法一中的第三層循環可以不要,每確定一組i,j(i<=j)都要通過for循環計算子序列的和。我們改進爲,這樣就能省略一個for循環來降低時間複雜度。


解法三:枚舉法終極版O(n)


由於子序列的左右界 i,j是不固定的,在解法一中,使用雙層for循環枚舉所有 i,j情況,每種情況的 i,j又使用for循環來計算子序列和,一共用了三層循環。在解法二中,用了for循環枚舉子序列左界 i的情況,又使用一層for循環列舉 j的所有情況下的最大序列和。而解法三中,只使用了一次for循環遍歷了N個元素,設置ThisSum表示當前的子序列和,每經歷一個元素便加進ThisSum,更新後的ThisSum與MaxSum比較,比他大則賦值,否則,如果比 0 小,則將ThisSum=0。本算法有許多好處:①時間複雜度只有O(n),基本上是最省時的算法。②它只對數據進行一次掃描,一旦A[i]被讀入並被處理,它就不再需要被記憶。因此,如果數組在磁盤或磁帶上,它就可以被順序讀入,在主存中不必存儲數組的任何部分。③在任意時刻,算法都能對它已經讀入的數據給出子序列問題的正確答案,稱爲聯機算法。


解法四:遞推分治算法O(nlog(n))

解法四使用了一種和前三種都不一樣的思想,也是算法分析中常用的兩種思想“遞推”和“分治”。對於N個元素的數組,將其分爲兩部分,則最大子序列和則是左部分的最大子序列和MaxLeftSum,右部分最大子序列和MaxRightSum,包含中間元素的最大子序列和MaxLeftBorderSum+MaxRightBorderSum,這三類中的最大值。6-7行計算左右兩部分的最大子序列和,利用遞推的方法。8-17行計算包含中間元素的最大子序列和,使用了兩個for循環時間單位爲O(n)。最終,時間複雜度爲O(nlog(n))。對於帶log的時間複雜度的分析,《數據結構與算法分析》這本書後面又舉了三個例子來說明,後面一篇文章會進行闡述。


結語:作者在本題解法結尾說了一句話:“僅需要常量空間並以限行時間運行的在線算法幾乎是完美的算法”,就是解法三。



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