O(nlgn)-time algorithm to find the LIS
In the spirit of dynamic programming, we first consider what information we would need from previous problems to solve for a longest increasing sub-sequence in A[n]. There are two cases:
Case 1. The length of the LIS(s) of A[n] is equal to the length of the LIS(s) of A[n-1].
        This will occur when for all A[k], where A[k] is the last element in a longest increasing sub-sequence 
        of A[n-1], A[n] <= A[k].
Case 2. The length of the LIS(s) of A[n] is equal to 1 + the length of the LIS(s) of A[n-1].
        This will occur when A[n] > than some A[k], where A[k] is the last element in a longest increasing 
        sub-sequence of A[n-1].Clearly Case (2) is desired, since we are increasing the length of the LIS. So what differentiates Cases (1) and (2)? Well, note that to be in Case (1), A[n] must be less than ALL of some elements A[k], whereas to be in Case (2), A[n] must simply be greater than ONE instance of A[k]. To maximize the chances of the latter case, then, we would want to keep track of the minimum A[k] that yields a longest sub-sequence of some number of elements.
To get the longest sub-sequence itself, we must also have a way to keep track of which elements are part of our current chain, since we are changing values. We do this by maintaining a new array that holds the previous element in the longest chain starting with the element that we’re using. This will be clearer in an example below.
Summary:
- In increasing indexing order though the array, compare a new element to the last element in the current - LIS. If the new element is larger, we attach to the- LIS. Update the array for previous values here. This runs in- O(n)time.
- If the new element is not larger, we find the smallest element in the - LISsequence that is larger than our value and replace it with our current value. We can find this element using binary search, since the values index by- I[ ]are in ascending order. This runs in- O(log(n))time.
- Trace back through the previous-index-array to recover the actual sub-sequence. This runs in - O(n)time.
Example:
A = [2, 4, 1, 8, 9, 5, 6, 7]
I[k] holds the index of the last element in some increasing sub-sequence(not longest increasing sub-sequence) of k values. And A[I[0]] < A[I[1]] < … < A[I[len(I) - 1]].
P[k] holds the index of the previous value in the longest increasing sub-sequence of k values.
2: I[ ] = {0}, P[ ] = {} LIS is {2} 4: I[ ] = {0, 1}, P[ ] = {0} LIS is {2, 4} 1: I[ ] = {2, 1}, P[ ] = {0} LIS is {2, 4} 8: I[ ] = {2, 1, 3}, P[ ] = {0, 1} LIS is {2, 4, 8} 9: I[ ] = {2, 1, 3, 4}, P[ ] = {0, 1, 3} LIS is {2, 4, 8, 9} 5: I[ ] = {2, 1, 5, 4}, P[ ] = {0, 1, 3} LIS is {2, 4, 8, 9} 6: I[ ] = {2, 1, 5, 6} P[ ] = {0, 1, 5} LIS is {2, 4, 5, 6} 7: I[ ] = {3, 2, 5, 6, 7} P[ ] = {0, 1, 5, 6} LIS is {2, 4, 5, 6, 7}
Pseudo Code:
| 1 | //Input: An array A | 
