Add longest increasing subsequence.

This commit is contained in:
Oleksii Trekhleb 2018-04-26 15:49:52 +03:00
parent 15e798c130
commit 626eb8a746
4 changed files with 137 additions and 2 deletions

View File

@ -40,6 +40,7 @@
* [Combinations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/combinations) (with and without repetitions) * [Combinations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/combinations) (with and without repetitions)
* [FisherYates Shuffle](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/fisher-yates) - random permutation of a finite sequence * [FisherYates Shuffle](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/fisher-yates) - random permutation of a finite sequence
* [Longest Common Subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-common-subsequnce) (LCS) * [Longest Common Subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-common-subsequnce) (LCS)
* [Longest Increasing subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-increasing-subsequence)
* **String** * **String**
* [Levenshtein Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences * [Levenshtein Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences
* [Hamming Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/hamming-distance) - number of positions at which the symbols are different * [Hamming Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/hamming-distance) - number of positions at which the symbols are different
@ -92,8 +93,7 @@
* [Levenshtein Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences * [Levenshtein Distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences
* [Longest Common Subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-common-subsequnce) (LCS) * [Longest Common Subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-common-subsequnce) (LCS)
* [Longest Common Substring](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/longest-common-substring) * [Longest Common Substring](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/longest-common-substring)
* Increasing subsequence * [Longest Increasing subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-increasing-subsequence)
* Longest Increasing subsequence
* Shortest common supersequence * Shortest common supersequence
* Knapsack problem * Knapsack problem
* Maximum subarray * Maximum subarray

View File

@ -0,0 +1,46 @@
# Longest Increasing Subsequence
The longest increasing subsequence problem is to find a subsequence of a
given sequence in which the subsequence's elements are in sorted order,
lowest to highest, and in which the subsequence is as long as possible.
This subsequence is not necessarily contiguous, or unique.
## Complexity
The longest increasing subsequence problem is solvable in
time `O(n log n)`, where `n` denotes the length of the input sequence.
Dynamic programming approach has complexity `O(n * n)`.
## Example
In the first 16 terms of the binary Van der Corput sequence
```
0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
```
a longest increasing subsequence is
```
0, 2, 6, 9, 11, 15.
```
This subsequence has length six;
the input sequence has no seven-member increasing subsequences.
The longest increasing subsequence in this example is not unique: for
instance,
```
0, 4, 6, 9, 11, 15 or
0, 2, 6, 9, 13, 15 or
0, 4, 6, 9, 13, 15
```
are other increasing subsequences of equal length in the same
input sequence.
## References
- [Wikipedia](https://en.wikipedia.org/wiki/Longest_increasing_subsequence)
- [Dynamic Programming Approach on YouTube](https://www.youtube.com/watch?v=CE2b_-XfVDk)

View File

@ -0,0 +1,36 @@
import dpLongestIncreasingSubsequence from '../dpLongestIncreasingSubsequence';
describe('dpLongestIncreasingSubsequence', () => {
it('should find longest increasing subsequence length', () => {
// Should be:
// 9 or
// 8 or
// 7 or
// 6 or
// ...
expect(dpLongestIncreasingSubsequence([
9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
])).toBe(1);
// Should be:
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
expect(dpLongestIncreasingSubsequence([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
])).toBe(10);
// Should be:
// -1, 0, 2, 3
expect(dpLongestIncreasingSubsequence([
3, 4, -1, 0, 6, 2, 3,
])).toBe(4);
// Should be:
// 0, 2, 6, 9, 11, 15 or
// 0, 4, 6, 9, 11, 15 or
// 0, 2, 6, 9, 13, 15 or
// 0, 4, 6, 9, 13, 15
expect(dpLongestIncreasingSubsequence([
0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15,
])).toBe(6);
});
});

View File

@ -0,0 +1,53 @@
/**
* Dynamic programming approach to find longest increasing subsequence.
* Complexity: O(n * n)
*
* @param {number[]} sequence
* @return {number}
*/
export default function dpLongestIncreasingSubsequence(sequence) {
// Create array with longest increasing substrings length and
// fill it with 1-s that would mean that each element of the sequence
// is itself a minimum increasing subsequence.
const lengthsArray = Array(sequence.length).fill(1);
let previousElementIndex = 0;
let currentElementIndex = 1;
while (currentElementIndex < sequence.length) {
if (sequence[previousElementIndex] < sequence[currentElementIndex]) {
// If current element is bigger then the previous one then
// current element is a part of increasing subsequence which
// length is by one bigger then the length of increasing subsequence
// for previous element.
const newLength = lengthsArray[previousElementIndex] + 1;
if (newLength > lengthsArray[currentElementIndex]) {
// Increase only if previous element would give us bigger subsequence length
// then we already have for current element.
lengthsArray[currentElementIndex] = newLength;
}
}
// Move previous element index right.
previousElementIndex += 1;
// If previous element index equals to current element index then
// shift current element right and reset previous element index to zero.
if (previousElementIndex === currentElementIndex) {
currentElementIndex += 1;
previousElementIndex = 0;
}
}
// Find the biggest element in lengthsArray.
// This number is the biggest length of increasing subsequence.
let longestIncreasingLength = 0;
for (let i = 0; i < lengthsArray.length; i += 1) {
if (lengthsArray[i] > longestIncreasingLength) {
longestIncreasingLength = lengthsArray[i];
}
}
return longestIncreasingLength;
}