LIS:最長上升子序列(3種方法)

最長上升子序列是我在計蒜客中的藍橋杯模擬題中遇到的。
當事看到題目自己想了想,和最長相同子序列差不多。然後自己寫了兩種解法
一種是採用遞歸式的方法(時間複雜度估計2^n),
另一種事採用了動態規劃的方式(時間複雜度n*n)。
後來得知還存在一種時間複雜度更加優秀的二分法(時間複雜度n * logn), 於是看了下面的博客。

參考博客
這篇博客中二分法說的還算清楚(反正我一遍就瞭解得差不多 ),但還是有一些缺憾(爲什麼要用二分查找來經行優化沒有說明)。
後來我自己研究了一小會有所明悟,故此總結

下面就直接進入二分法:
假設有一段序列A2 7 8 4 9 5 3
再構建一個數組B:用來查詢最優結果。
對於LIS來,說它的結果序列必定是一個由小到大的遞增序列。(最重要的是有序的)。
於是對與數組B來說那麼它也必定是一個有序的。
我們不妨每次選取最大的放在後面, 小的放在前面來經行查詢。
B[0]初始化爲最小值。
然後經行3輪選取,後就有了下面的數組B:2 7 8

在後面繼續經行選取時會遇到47是大於4的第一個數,於是可以將7直接替換成4
注意:這裏的替換並不會影響最長上升子序列的長度(因爲替換並沒有改變序列的相對位置)。
接下來繼續後面的選取:

看到這裏是不是覺得so easy 啊!
是的,只需要掃描一遍數組A, 然後每次在B中找到需要替換, 或者增加的位置就行。
這裏就需要二分查找法。找到在序列B中第一個大於或等於當前關注點的位置。如果末尾值小於當前關注點(那麼就可以直接添加到末尾)。

最後B數組的長度就是LIS的結果了。

最後 show my code。

/*找到第一個比隊列中大的位置*/
int binary_search_upper(int* A, int left, int right, int key){
	if(A[right] < key){
		return right;
	}
	if(A[right] == key){
		return right;
	}
	int mid;
	while(right > left){
		mid = left + (right - left) / 2;
		if(A[mid] <= key){
			left = mid + 1;
		}else{
			right = mid;
		}
	}
	return left;
} 
int LIS_nlogn(int* A, int n){
	
	int* B = new int[n + 1];
	int len = 0;
	int next;
	B[0] = M;
	for(int i = 0; i < n; i++){
		next = binary_search_upper(B, 0, len - 1, A[i]);//  找到在序列B中第一個大於或等於當前關注點的位置
		B[next] = A[i];
		if(next >= len - 1){// 記錄長度, 以免後面還需要掃描一遍查詢長度 
			len = next + 1; // 這裏記錄的時長度, next是下標,所以需要加減1
		}
	}
	delete []B;
	return len;
}

end

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章