有人可以解释为什么以下LIS算法不是O(n)?

时间:2014-11-21 10:36:48

标签: algorithm erlang dynamic-programming combinatorics lis

以下代码遍历列表一次并找到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 ])).

2 个答案:

答案 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 41 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)的原因。