最近幾天好好的研究了一下這個問題。
問題本身就不多說了,求一串數字中的所有子串中,和最大的乙個子串。例如:
輸入:-10 5 2 3 4 -5 -23 3 7 -21
輸出:14 5 4
一、各種方法
方法1:
maxsofar = 0for i = [0
,n)
for j =[i,n)
sum = 0
for k =[i,j]
sum +=x[k]
maxsofar = max(maxsofar,sum)
這是最直接最暴力的方法,我沒有寫,時間複雜度為o(n3),明顯這裡面有很多重複的運算,我們可以很容易的把時間複雜度降為o(n2)。實際上,我第一印象就是下面這個
方法2:
ints,e;
int maxsum = -999999
;int tempsum = 0
;for(int i=0;i)
}}
這個方法理解為,以x[i]開頭的所有子串中,和最大的乙個。比列舉所有的 i 和 j 減少了計算量。下面乙個比較重要的方法,雖然在時間複雜度上面沒有提高,卻包含了乙個應對區間問題很重要的技巧。
方法3:
cumarr[-1] = 0for i = [0
,n) cumarr[i] = cumarr[i-1] +x[i]
maxsofar = 0
for i = [0
,n)
for j =[i,n)
sum = cumarr[j] - cumarr[i-1
] maxsofar = max(maxsofar,sum)
這個演算法中,比較重要的一點事注意到子串和x[i...j] = cumarr[j] = cumarr[i-1],這個經驗可以用在區間問題上。乙個簡單的列子:
n個收費站之間有n-1段路,每段路花費為p,用o(1)時間求任意兩個收費站的之間的花費,要求空間為o(n)。萬能的二分能不能用到這個問題上,顯然是可以的。在這個二分的過程中,需要注意的就是,合併結果的時候,需要注意到,跨左邊跟右邊的子串和的計算,最後就是在左邊的最大子串、右邊的最大子串、中間跨界的最大串這三者中取最大值。
方法4:
1int max_sub(int m,intn)2
21}22int
rmax,b;
23 rmax = sum = b = 0;24
for(int i=k+1;i<=n;i++)
2532}33
int max_l =max_sub(m,k);
34int max_r = max_sub(k+1
,n);
35int result = max(lmax+rmax,max(max_l,max_r));
36/*
37how to record the start and the end
38*/
39return
result;
40 }
對於這個二分,還有乙個待解決的問題,我想嘗試一下,記錄最大子串的起始位置和結束位置。個人對這種遞迴的理解確實不夠,還沒能夠實現記錄起始和結束位置。看來我還是得抽空好好把遞迴這個玩意兒好好理解一下。二分的話,顯然時間複雜度為o(n*logn)。
接下來,就是o(n)的方法了,再來回顧一下方法3中,sum = cumarr[j] - cumarr[i-1]。要使得sum的值最大,顯然cumarr[j]越大,cumarr[i-1]的值越小,sum的值越大。於是我們可以遍歷cumarr陣列,維持乙個最小的cumarr[min_s],然後取cumarr[j]-cumarr[min_s]的最大值。
方法5:
1 min_s = -1//這裡要初始化為-1
2 cumarr[-1] = 0;3
for(int i=0;i)
4 cumarr[i] = cumarr[i-1] +input[i];
5for(int i=0;i)614
if(cumarr[min_s] >cumarr[i])
15 min_s =i;
16 }
這裡還有乙個方法6:
1int start = 0;2
for(int i=0;i)38
else
if(maxendinghere + input[i] <= 0)9
13if(maxendinghere >maxsofar)
1419 }
-10 1 2 3 4 -5 -23 3 7 -21 (num)這樣就比較好理解第六種方法。-10 | 1 3 6 10 8 | -23 | 3 10 | -21 (sum)(|號為該處sum被清零)
由於10是sum的最大值,所以,紅色的那塊就是要求的範圍了。
二、思考問題:
1.證明最大子串和的時間複雜度下屆是o(n)
各位如果有思路或者資料麻煩告訴我一聲。。。無處下手啊
2.求子串和最接近0的子串。
嗯,對於這個問題,前面的經驗有 sum = cumarr[j]-cumarr[i-1],這樣的話問題就轉換成,求cumarr陣列裡面差值最小或者相等的兩個元素。
用排序的方法,再遍歷一次陣列就可以得出結果,時間複雜度為o(n*logn)。 有沒有更好的方法?
3.求子串和最接近t的子串。
這個問題,如果繼續採用問題二的方案,問題轉換成,在乙個排好序的陣列裡面找兩個值的差值為t,顯然不能達到同樣的效果。暫時沒有想到其他更好的方法。
4.m和n為整數,給定x[n],求整數i,使得 x[i] + ... + x[i+m] 的和最接近為0。(即在第二個問題的基礎上,加了條件: m個元素)
這個問題因為相差為m,掃一遍cumarr陣列貌似就ok了。
min =max_numcumarr[-1] = 0
for i = [0
,n)
if(i+mtemp = cumarr[i+m]-cumarr[i-1
]
if (min >temp)
min =temp
start =i
end = i+m
想要透徹的理解乙個演算法真的很難,我感覺我還是不太會思考。上面大部分內容是參考程式設計珠璣上面的內容,看懂正文好像不是太難,但是後面的習題就各種傻了。也許是我看得太少的緣故吧。
接下來想要看得主題 column 4 writing correct programs
最大子串和問題
問題 給定一組數字,求連續的字串的最大的和。這裡要注意題目中是子串和而不是子串行和。子串行只要求各元素的順序與其在陣列中一致,而沒有連續的要求。如果求子序列,可直接把這組數字中的正數相加即可。最開始想著只要把這組數字中各個正數子串行分別求和,比較哪個大就行了,後來發現不對。比如 正數子串行分別為,其...
最大子串和問題(Maximum Subarray)
又乙個經典問題,對於乙個包含負值的數字串array 1.n 要找到他的乙個子串array i.j 0 i j n 使得在array的所有子串中,array i.j 的和最大。這裡我們需要注意子串和子串行之間的區別。子串是指數組中連續的若干個元素。子串行只要求各元素的順序與其在陣列中一致,而沒有連續的...
最大子串問題
例如 2,4,7,20,1,1,1,1,10,1,1,1,10,10,10,10,25,10,10,10,10,10,10,300 首先,看到這個問題我們最先想到的一種方法就是 找到所有的子串,然後通過依次比較,找到最大的。接下來我們演示一下這種方法的 public int getmaxvalue ...