1

The following code traverses the list once and finds the LIS. I don't see why the DP algorithm should take 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 Answers2

2

Your current implementation of lis/1 function is O(n), I don't see any reasons to doubt. But there is some problem. You implementation doesn't actually calculate valid LIS. Try

lis(lists:reverse([1,2,3,4,1,2]))

for error example. Longest increasing sequense is [1,2,3,4], right? But your algorith returns 6 as result.

  • First error in your algorithm in that you increase result each time, when you encountered element that greater than previous. But you should increase result only if current element is greater that greatest element of your current LIS. So (according example above) you should remember 4 and not increase result after you detected then 2 is greater than 1.

  • But this not only thing you have to do. Consider sequence 1 2 3 6 4 5. For 5 first elements LIS has length of 4. But there is TWO possible options there - either 1 2 3 4 or 1 2 3 6. Which you should take as actual LIS?

  • And so on, and so on...

Another example comes directly from wiki page.

Sequence [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15] has LIS [0, 2, 6, 9, 13, 15] (e.g., 6 elements), but your algorythm says 9.

And, (correct me, if I'm wrong), LIS implementation must return subsequence itself, but not only its length.

Viacheslav Kovalev
  • 1,745
  • 12
  • 17
  • Thank you Viacheslav Kovalev and @Roberto Aloi! Can you please point out why it works for my example and not for all cases? I think, Returning the subsequence along with length using the above algorithm only takes additional space and not additional time. – functional_overflow Nov 21 '14 at 12:35
  • @functional_overflow, I've updated (actually, corrected) example that demostrates your algorith misbehaviour. – Viacheslav Kovalev Nov 21 '14 at 12:59
  • Thanks, i understood that my algorithm is wrong. I don't understand how/where it is wrong. I was hoping you would point out the mistake in my solution. – functional_overflow Nov 21 '14 at 13:43
  • @functional_overflow I've tried to briefly explain some of your algorithm flaws. But I'd recommend your to start from original O(n^2) algorithm and try to get which problems does it solve. – Viacheslav Kovalev Nov 21 '14 at 14:18
2

Good news: the above implementation is not quadratic.

Bad news: the above function does not calculate the longest subsequence of increasing elements (defined as the LIS).

To respond to your question, the actual LIS problem has quadratic complexity, not the DP algorithm itself, which is merely a step towards the final solution. In fact, the DP algorithm is repeated for all elements of the list (you need to calculate all the DP[i]), which is why the complete algorithm is categorized as O(n^2).

Roberto Aloi
  • 30,570
  • 21
  • 75
  • 112