已經正式開始學習資料結構和演算法,先學了網易雲課堂上的浙江大學的資料結構課,是陳越和何欽銘上的,了解了什麼是資料結構和演算法後,學習了一些時間空間複雜度分析的技巧,結合之前馬虎掌握的學習,先從簡單的題目入手學習。
題目是這樣的:
給定了乙個n個整數組成的序列,求它各個子列中,子列和最大的值。做出這題的難度不是很大,至少很容易可以做到暴力求解,然而暴力求解的時間複雜度是很大的。輸入:輸入n個整數組成的序列
要求輸出最大子列和。
示例:輸入:
-211 -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
這個演算法看到**後推敲就容易理解這個思路了。不詳述。
同時,為了測試這些演算法,寫了乙個生成隨機數表的函式和測試函式,測試各個函式的執行時間和結果:
importrandom
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()
'\n%r:
' % fcn.__name__
'num :%d
'%num
'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,...