【算法】找最长的降序子序列

找数组中最长的降序子序列(不要求连续的)。我一听这个题觉得很熟悉,因为我们做多了找数组的最长上升子序列。就是将数组逆序过来,找最长的上升子序列即可。但是我们的时间复杂度是O(NN)的,但是今天我们求的这个最长降序方法的时间复杂度是O(NlgN)的。

lower_bound的详细使用说明见https://blog.csdn.net/weixin_43939593/article/details/105602530

方法一:通过库函数lower_bound来实现

算法思想:我们有一个数组 {11,3,4, 3.5, 1}, 我们再找最长降序子序列的时候,11和3满足降序。到 3 不小于4,所以我们还要往下找,但是现在的最长子序列为2。接下来我们分情况看:

  1. 4又比3大,如果接下来4开始的话(不要11,3),那么加上接下来的3.5以后,我们的最长序列就是2, (4,3)
  2. 如果4比3大,不要4,那么就是 11 3 ,接下来是3.5,还是不满足,那么就是最长的子序列就是2 (11,3)
    所以 我们要将4覆盖到3上,这样向后找 就可以满足到3.5的时候,序列长度为最长的3(11,4,3.5)。
    原因是: 当11和3组合是长度为2,11和4组合长度也是2,但是后面的元素有可能比3大比4小,那么就要比较大的数4覆盖到3上,最终肯定能得到最优解。

基于这种思想。所以我们就可以写出下面的代码:

算法实现

int main() {
	vector<int> b = { 11, 3, 12, 4, 1, 9, 8, 5 };
	vector<int> dp(b.size());
	int len = 0;
	dp[0] = b[0];
	for (int i = 1; i < b.size(); i++) {
		if (b[i] < dp[len]) {      // 如果b[i] 小于dp[len],则直接加入到后面就行了
			dp[++len] = b[i]; 
		}
		else
		{  // 找个合适的位置,让它覆盖掉原来的值
			dp[(lower_bound(dp.begin(), dp.begin() + b.size(), b[i], greater<int>()) - dp.begin())] = b[i];
		}
	}
	cout << len + 1 << endl; // 因为len是数组下标,数组还有0,所以len+1

	system("pause");

}

总结: else语句中的意思是,从dp数组中找到dp数组中的第一个位置使得b[i]大于等于它,因为lower_bound中是一个迭代器,所以要减去dp.begin()。

时间复杂度:O(NlogN),遍历了一遍数组O(N),但是lower_bound的时间复杂度是O(lgN)(下节会讲)。所以总的时间复杂度就是O(NlogN)

空间复杂度:O(N). 额外数组dp的开辟。

猜你喜欢

转载自blog.csdn.net/weixin_43939593/article/details/105601631