以下代码遍历列表一次并找到LIS。我不明白为什么DP算法应该采用O(n2)。
//C
int lis(int *a, int l){
if(l == 0) return 1;
else if(a[l] > a[l - 1]) return 1 + lis(a, l - 1);
else return lis(a, l - 1);
}
int main(){
int a[] = { 10, 22, 9, 33, 21, 50, 41, 60 };
cout << lis(a, sizeof(a)/sizeof(a[0]) - 1);
}
% erlang
lis([_H]) -> 1;
lis([H1, H2 |T]) when H1 > H2 -> 1 + lis([H2|T]);
lis([_H|T]) -> lis(T).
main() -> lis(lists:reverse([ 10, 22, 9, 33, 21, 50, 41, 60 ])).
答案 0 :(得分:2)
您当前的lis/1
函数的实现是O(n),我认为没有任何理由怀疑。但是有一些问题。您的实现实际上并不计算有效的LIS。试试
lis(lists:reverse([1,2,3,4,1,2]))
表示错误示例。最长的序列是[1,2,3,4],对吗?但是你的算法会返回6作为结果。
算法中的第一个错误是,当您遇到大于之前的元素时,每次增加result
。但是,只有当前元素大于当前LIS的最大元素时,才应增加result
。所以(根据上面的例子)你应该记住4
并且在检测到result
大于2
之后不会增加1
。
但这不仅仅是你要做的事情。考虑序列1 2 3 6 4 5
。对于5个第一个元素,LIS的长度为4.但是有两个可能的选项 - 1 2 3 4
或1 2 3 6
。你应该把它作为实际的LIS?
依旧等等......
另一个例子直接来自wiki page.
序列[0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]
包含LIS [0, 2, 6, 9, 13, 15]
(例如,6
个元素),但您的algorythm说9
。
并且,(纠正我,如果我错了),LIS实现必须返回子序列本身,但不仅仅是它的长度。
答案 1 :(得分:2)
好消息:以上实施不是二次方。
坏消息:上述函数不计算增加元素的最长子序列(定义为LIS)。
为了回答您的问题,实际的LIS问题具有二次复杂性,而不是DP算法本身,这仅仅是朝向最终解决方案迈出的一步。实际上,对列表的所有元素重复DP算法(您需要计算所有DP [i]),这就是完整算法被归类为O(n ^ 2)的原因。