1. 程式人生 > >算法實踐--最長遞增子序列(Longest Increasing Subsquence)

算法實踐--最長遞增子序列(Longest Increasing Subsquence)

pan 是否 ring 所有 時間復雜度 n) clas 遞推公式 string

什麽是最長遞增子序列(Longest Increasing Subsquence)

對於一個序列{3, 2, 6, 4, 5, 1},它包含很多遞增子序列{3, 6}, {2,6}, {2, 4, 5}, {1}

其中最長的遞增子序列是{2, 4, 5}

問題:對於長度為N的矢量D,如何找到它的最長遞增子序列

一個簡單的算法

for (i=N; i>0; --i) {1. 找到所有長度為i的子序列;   //復雜度為(N!)/(i!)(N-i)!    O(exp(N))
   2. 判斷是否其中有一個為遞增子序列
}

動態規劃算法

基本思想:將一個復雜問題,分解為更小的子問題

首先定義LIS[i],表示以D[i]結尾的最長子序列

對於矢量D = {3, 2, 6, 4, 5, 1};

LIS[0]:  3
LIS[1]:  2
LIS[2]:  2,6
LIS[3]:  2,4
LIS[4]:  2,4,5
LIS[5]:  1

給出遞推公式

LIS[0] = D[0]

LIS[i] = MAX(LIS[j] | j <i, D[j] <D[i]) + "D[i]"

解釋:

當我們求LIS[i]時,對於任意j<i,LIS[j]都已知

在所有已知的LIS中,找出結尾的元素值小於D[i],長度最長的一個

然後在後面加上D[i]元素,即為LIS[i]

示例C++代碼

using namespace
std; void prt(vector<int>& arr, string msg = "") { cout << msg << " "; for (auto i: arr) { cout << i << " "; } cout << endl; } void calc_LIS(vector<int>& D) { vector< vector<int> > L(D.size()); // The longest increasing subsequence ends with D[i]
L[0].push_back(D[0]); for (int i=1; i<D.size(); i++) { for(int j=0; j<i; j++) { if ( (D[j] < D[i]) && ( L[i].size() < L[j].size() ) ) { L[i] = L[j]; } } L[i].push_back(D[i]); } for (auto x: L) { prt(x); } } int main() { int a[] = {3, 2, 6, 4, 5, 1}; vector<int> arr(a, a + sizeof(a)/sizeof(a[0])); prt(arr, "Data In:"); calc_LIS(arr); return 0; }

復雜度分析

時間復雜度是O(N^2)

動態規範的基本思想是以一定的空間開銷,顯著縮短時間開銷

算法實踐--最長遞增子序列(Longest Increasing Subsquence)