原文見
分析過程很清楚。這裡主要是**部分有改動。完全用c寫的,從檔案中讀入。另外,解法二的程式加了去重,求的是最長單調遞增子串行。
求陣列中最長遞增子串行
寫乙個時間複雜度盡可能低的程式,求乙個一維陣列(
n個元素)中的最長遞增子串行的長度。
例如:在序列1,
-1,2,
-3,4,
-5,6,
-7中,其最長的遞增子串行為1,
2,4,
6。
分析與解法
根據題目的要求,求一維陣列中的最長遞增子串行,也就是找乙個標號的序列
b[0],b[1],
…,b[m](0 <= b[0] < b[1] <
…< b[m] < n)
,使得array[b[0]]…。
解法一
根據無後效性的定義我們知道,將各階段按照一定的次序排列好之後,對於某個給定的階段狀態來說,它以前各階段的狀態無法直接影響它未來的決策,而只能間接地通過當前的這個狀態來影響。換句話說,每個狀態都是歷史的乙個完整總結。
同樣的,仍以序列1,
-1,2,
-3,4,
-5,6,
-7為例,我們在找到
4之後,並不關心
4之前的兩個值具體是怎樣,因為它對找到
6沒有直接影響。因此,這個問題滿足無後效性,可以通過使用動態規劃來解決。
可以通過數字的規律來分析目標串:1,
-1,2,
-3,4,
-5,6,
-7。
使用i來表示當前遍歷的位置
當i=1時,顯然,最長的遞增序列為(
1),序列長度為1.
當i=2
是,由於
-1<1
。因此,必須丟棄第乙個值後重新建立串。當前的遞增序列為(
-1),長度為1。
當i=3時,由於
2>1,2>-1
。因此,最長的遞增序列為(
1,2)
,(-1,2),
長度為2
。在這裡,
2前面是1還是
-1對求出後面的遞增序列沒有直接影響。(但是在其它情況下可能有影響)
依此類推之後,我們得出如下的結論。
假設在目標陣列
array的前i
個元素中,最長遞增子串行的長度為
lis[i]
。那麼,
lis[i+1]=max,array[i+1]>array[k],for any k <= i
即如果array[i+1]
大於array[k],
那麼第i+1
個元素可以接在
lis[k]
長的子串行後面構成乙個更長的子串行。於此同時
array[i+1]
本身至少可以構成乙個長度為
1的子串行。
根據上面的分析,就可以得到**清單
這種方法的時間複雜度為o(n2 + n) = o(n2 )
解法二
在前面的分析中,當考察第i+1 個元素的時候,我們是不考慮前面i 個元素的分布情況的。現在我們從另乙個角度分析,即當考察第i+1 個元素的時候考慮前面i 個元素的情況。
對於前面i 個元素的任何乙個遞增子串行,如果這個子串行的最大的元素比array[i+1] 小,那麼就可以將array[i+1] 加在這個子串行後面,構成乙個新的遞增子串行。
比如當i=4 的時候,目標序列為1,
-1,2,
-3,4,
-5,6,
-7最長遞增序列為
(1,2),(-1,2)。
那麼,只要
4>2,
就可以把
4直接增加到前面的子串行中形成乙個新的遞增子串行。
因此,我們希望找到前
i個元素中的乙個遞增子串行,使得這個遞增子串行的最大的元素比
array[i+1]
小,且長度盡量地長。這樣將
array[i+1]
加在該遞增子串行後,便可以找到以
array[i+1]
為最大元素的最長遞增子串行。
仍然假設在陣列的前
i個元素中,以
array[i]
為最大元素的最長遞增子串行的長度為
lis[i]。
同時,假設:
長度為1的遞增子串行最大元素的最小值為
maxv[1];
長度為2的遞增子串行最大元素的最小值為
maxv[2];
……
長度為lis[i]
的遞增子串行最大元素的最小值為
maxv[lis[i]];
本迴圈不變式p是:
p:k是序列
a[0:i]
的最長遞增子串行的長度,
0≤i <n 。
容易看出,在由i-1 到i 的迴圈中,a[i] 的值起關鍵作用。如果a[i] 能擴充套件序列a[0;i-1] 的最長遞增子串行的長度,則k=k+1 ,否則k 不變。設a[0;i-1] 中長度為k 的最長遞增子串行的結尾元素是a[j](0 ≤j ≤i-1) ,則當a[i] ≥a[j] 時可以擴充套件,否則不能擴充套件。如果序列a[0;i-1] 中有多個長度為k 的最長遞增子串行,那麼需要儲存哪些資訊?容易看出,只要儲存序列a[0;i-1] 中所有長度為k 的遞增子串行中結尾元素的最小值b[k] 。因此,需要將迴圈不變式p 增強為:
p:0 ≤i <n;k 是序列a[0;i] 的最長遞增子串行的長度;
b[k]
是序列a[0;i] 中所有長度為k 的遞增子串行中最小結尾元素值。
相應地,歸納假設也增強為:已知計算序列a[0;i-1](i <n) 的最長遞增子串行的長度k 以及序列a[0;i] 中所有長度為k 的遞增子串行中的最小結尾元素值b[k] 的正確演算法。
增強歸納假設後,在由i-1 到i 的迴圈中,當a[i] ≥b[k] 時,k=k+1 ,b[k]=a[i], 否則k 值不變。注意到當a[i] ≥b[k] 時,k 值增加,b[k] 的值為a[i] 。那麼,當a[i] <b[k] 時,b[l;k] 的值應該如何改變?如果a[i]則顯然應該將b[l] 的只改變為a[i], 當b[l] ≤a[i] ≤b[k] 時,注意到陣列b 是有序的,可以用二分搜尋演算法找到下標j ,使得b[j-1] ≤a[i] ≤b[j] 。此時,b[1;j-1] 和b[j+1;k] 的值不變,b[j] 的值改變為a[i] 。
求陣列中最長遞增子串行
最長遞增子串行,longest increasing subsequence 下面我們簡記為 lis。排序 lcs演算法 以及 dp演算法就忽略了,這兩個太容易理解了。假設存在乙個序列d 1.9 2 1 5 3 6 4 8 9 7,可以看出來它的lis長度為5。下面一步一步試著找出它。我們定義乙個序...
求陣列中最長遞增子串行
根據 程式設計之美 中解法二的思路,發現記錄lis陣列是不必要的,只要直接不斷更新maxv即可。在遍歷整個陣列arr的過程中,maxv陣列的長度也在不斷增加。當遍歷到arr i 時,maxv j 中已經記錄了由arr 0 arr i 的序列可以得到的所有長度為j的子串行中最大元素的最小值。例如 ar...
求陣列中最長遞增子串行
程式設計之美有一道關於陣列中最長遞增子串行,題目如下 寫乙個時間複雜度盡可能低的程式,求乙個一維陣列 n個元素 中最長遞增子串行的長度。例如在序列1,1,2,3,4,5,6,7中,其最長的遞增子串行的長度為4 如1,2,4,6 從該書給的例子我們可以知道的是其最長的遞增子串行可以不連續的。作者利用動...