2015 10 13 演算法習題 最大子列和問題

2022-03-24 13:46:49 字數 4173 閱讀 1276

已經正式開始學習資料結構和演算法,先學了網易雲課堂上的浙江大學的資料結構課,是陳越和何欽銘上的,了解了什麼是資料結構和演算法後,學習了一些時間空間複雜度分析的技巧,結合之前馬虎掌握的學習,先從簡單的題目入手學習。

題目是這樣的:

給定了乙個n個整數組成的序列,求它各個子列中,子列和最大的值。

輸入:輸入n個整數組成的序列

要求輸出最大子列和。

示例:輸入:

-2

11 -4

13 -5 -2

輸出:

20

做出這題的難度不是很大,至少很容易可以做到暴力求解,然而暴力求解的時間複雜度是很大的。

我用python寫了暴力求解的方法:

def maxsubseqsum1(list):  #

o(n^3)

lenth = len(list)maxsum =0

for i in

range(lenth):

for j in

range(lenth):

thissum =0

for count in range(i,j+1):

thissum +=lst[count]

if thissum >maxsum:

maxsum =thissum

print maxsum

暴力求解的邏輯很直接,就是按順序將所有子列和求出來依次比較。

由於有三重巢狀迴圈,所以執行時間是輸入序列長度規模n的三次方,時間複雜度是o(n^3)很顯然,這是很差勁的乙個演算法,在輸入量較大時就求不出結果了。

讓我們分析一下這個演算法執行的過程,可以很明顯地看到乙個改進的地方:

我們讓 i 作為子列的首座標, j 作為子列的尾座標,依次遞增。

然後 count 在 i 和 j 之間,求i j 之間所有子列的和,比較最大值。

當 i 為 0 ,j 為 0 時,求了lst[0]的值。

當 i 為 0 ,j 為 1 時,求了lst[0], lst[0]+lst[1]的值。

當 i 為 0 ,j 為 2 時,求了lst[0], lst[0]+lst[1], lst[0]+lst[1]+lst[2]的值。

我們很容易就發現了乙個問題,我們重複計算了前面的值,也就是說,最後乙個 count 的 for 迴圈是根本沒有意義的,只是來增加了演算法的時間而已。

當然,此處是故意新增的這個問題,正常情況下不會如此寫這個問題。

於是,有了第二個演算法,去除count的迴圈:

def maxsubseqsum2(list):  #

o(n^2)

maxsum =0

lenth =len(list)

for i in

range(lenth):

thissum =0

for j in

range(i,lenth):

thissum +=list[j]

if thissum >maxsum:

maxsum =thissum

return maxsum

這個演算法時間複雜度是o(n^2),是正常想到的最常規的解法。

一般乙個時間複雜度是o(n^2)的問題,都會想把它改成o(nlogn)的問題。

考慮分而治之的方法是否可行,如果採用分治法,需要講問題規模減小。

求乙個序列最大子列和,是前1/2序列的最大子列和,後1/2序列最大子列和,跨中間邊界的最大子列和,三個數的最大值。這樣子不斷分隔,自然可以使用分治法。

舉例如下:

乙個序列是[-1, 2, 7, -3] 這個序列的最大子列和是下面這三個數中最大的:

[-1, 2]這個序列的最大子列和, [7, -3]這個序列的最大子列和, 經過2,7邊界的序列的最大和。

同理,[-1, 2]這個序列的最大子列和,是[-1],[2],和經過-1,2邊界的序列的最大和。

將乙個問題分解成乙個易於解決的問題(經過邊界的序列的最大和)和兩個規模較小的原問題。

而經過邊界的序列的最大和是很簡單的,等於從中間開始向左遍歷的最大序列和,以及從中間開始向右遍歷的最大序列和,然後將左右最大值相加。

以下是實現**:

def maxsubseqsum3(list):  #

o(nlogn)

lenth =len(list)

defmaxsum(list, left, right):

if left ==right:

if list[left] >0:

return

list[left]

else

:

return

0

else

: center = int((left+right)/2)

maxleftsum =maxsum(list, left, center)

maxrightsum = maxsum(list, center+1, right)

maxleftbordersum =0

leftbordersum =0

for i in range(center, left-1, -1):

leftbordersum +=list[i]

if leftbordersum >maxleftbordersum:

maxleftbordersum =leftbordersum

maxrightbordersum =0

rightbordersum =0

for i in range(center+1, right+1):

rightbordersum +=list[i]

if rightbordersum >maxrightbordersum:

maxrightbordersum =rightbordersum

return

max(maxleftsum, maxrightsum,\

maxleftbordersum +maxrightbordersum)

return maxsum(list, 0, lenth-1)

這個演算法的時間複雜度具體求解過程不在這裡展開,是o(nlogn)

一般來說乙個o(nlogn)的演算法已經足夠優秀,但是這個問題其實還有o(n)的演算法,也是最快的演算法了,因為必須遍歷資料才能知道大小:

def maxsubseqsum4(list):  #

o(n)

maxsum =0

lenth =len(list)

thissum =0

for i in

range(lenth):

thissum +=list[i]

if thissum >maxsum:

maxsum =thissum

elif thissum <0:

thissum =0

return maxsum

這個演算法看到**後推敲就容易理解這個思路了。不詳述。

同時,為了測試這些演算法,寫了乙個生成隨機數表的函式和測試函式,測試各個函式的執行時間和結果:

import

random

import

time

defmakeintseq(n, low, high):

list =

for i in

range(n):

return

list

....

deftest(n, low, high):

lst =makeintseq(n, low, high)

for fcn in

[maxsubseqsum4,maxsubseqsum3,maxsubseqsum2]:

start =time.clock()

num =fcn(lst)

end =time. clock()

print

'\n%r:

' % fcn.__name__

print

'num :%d

'%num

print

'time:

',end - start

由於第一種演算法能力太弱,沒有測試,事實上加入它時,當n在1000左右時就要等待時間才能得到結果了。大家可以試試執行時間。

最大子串演算法

最大子串問題是一類經典問題,即在一串整形陣列中選取和最大的子串 給出問題描述 對於乙個包含負值的數字串array 1.n 要找到他的乙個子串array i.j 0 i j n 使得在array的所有子串中,array i.j 的和最大。針對本問題,可有三種方法,一種是暴利破解列舉演算法,所有子串種類...

計蒜客習題 最大子陣

給定乙個n m 的矩陣 a,求a 中的乙個非空子矩陣,使這個子矩陣中的元素和最大。其中,a 的子矩陣指在 a 中行和列均連續的一部分。輸入格式 輸入的第一行包含兩個整數 n,m 1 n,m 50 分別表示矩陣 a 的行數和列數。接下來 n 行,每行 m 個整數,表示矩陣 a 1000 i,j 100...

計蒜客習題 最大子陣

問題描述 給定乙個n m 的矩陣 a,求a 中的乙個非空子矩陣,使這個子矩陣中的元素和最大。其中,a 的子矩陣指在 a 中行和列均連續的一部分。輸入格式 輸入的第一行包含兩個整數 n,m 1 n,m 50 分別表示矩陣 a 的行數和列數。接下來 n 行,每行 m 個整數,表示矩陣 a 1000 i,...