mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-12-26 07:01:18 +08:00
Add longest increasing subsequence.
This commit is contained in:
parent
15e798c130
commit
626eb8a746
@ -40,6 +40,7 @@
|
||||
* [Combinations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/combinations) (with and without repetitions)
|
||||
* [Fisher–Yates 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 Increasing subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-increasing-subsequence)
|
||||
* **String**
|
||||
* [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
|
||||
@ -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
|
||||
* [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)
|
||||
* Increasing subsequence
|
||||
* Longest Increasing subsequence
|
||||
* [Longest Increasing subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-increasing-subsequence)
|
||||
* Shortest common supersequence
|
||||
* Knapsack problem
|
||||
* Maximum subarray
|
||||
|
46
src/algorithms/sets/longest-increasing-subsequence/README.md
Normal file
46
src/algorithms/sets/longest-increasing-subsequence/README.md
Normal 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)
|
@ -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);
|
||||
});
|
||||
});
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user