最長上升子序列(Longest increasing subsequence)
阿新 • • 發佈:2018-12-16
問題描述 對於一串數A={a1a2a3…an},它的子序列為S={s1s2s3…sn},滿足{s1<s2<s3<…<sm}。求A的最長子序列的長度。
動態規劃法
演算法描述: 設數串的長度為n,L[i]為以第i個數為末尾的最長上升子序列的長度,a[i]為數串的第i個數。 L[i]的計算方法為:從前i-1個數中找出滿足a[j]<a[i](1<=j<i)條件的最大的L[j],L[i]等於L[j]+1。 動態規劃表示式:
程式碼實現:
int LIS(int a[], int n) { int len[MAXSIZE]; int i, j; int maxlen = 0; //計算以第i個數為結尾的最長上升子序列的長度 for (i = 1; i <= n; i++) { len[i] = 0; //從前i-1個數中找出滿足a[j]<a[i](1<=j<i)條件的最大的L[j] for (j = i-1; j >= 1; j--) { if (a[j] < a[i] && len[j] > len[i]) { len[i] = len[j]; } } len[i]++; if (len[i] > maxlen) { maxlen = len[i]; } } return maxlen; }
上述演算法的時間複雜度為O(n2)。
改進演算法: 在從前i-1個數中找出滿足a[j]<a[i](1<=j<i)條件的最大的L[j]的時間複雜度為O(n),這裡採用二分查詢的方法對它進行優化,使其複雜度降為O(nlogn)。 增設一個m[]陣列,m[x]存放長度為x的最長上升子序列的最小末尾數。例:m[3] = 17表示長度為3的最長上升子序列的最小末尾數為17。 由於子序列是上升的,所以m陣列中的元素有一個性質,當x<y時,m[x]<m[y],利用這個性質來使用二分查詢。 設m陣列所儲存的最長上升子序列的長度為k,當前計算的數為第i個 如果a[i]>m[k],則m[++k]=a[i]; 否則在m[1~k]內二分查詢小於(等於)a[i]的最大值的位置p,m[p]=a[i]。
程式碼實現:
int BSearch(int a[], int n, int t) { int low = 1; int high = n; while (low <= high) { int mid = (low + high) / 2; if (t == a[mid]) { return mid; } else if (t > a[mid]) { low = mid + 1; } else { high = mid - 1; } } return low; } int LIS_BSearch(int a[], int m[], int n) { int maxlen = 1; //最長上升子序列的長度 m[maxlen] = a[1]; int i; for (i = 2; i <= n; i++) { if (a[i] > m[maxlen]) { m[++maxlen] = a[i]; } else { //返回小於a[i]的最大值的位置p int p = BSearch(m, maxlen, a[i]); m[p] = a[i]; } } return maxlen; }
改進後的演算法時間複雜度為O(nlogn)。