mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-12-26 23:21: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)
|
* [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
|
* [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 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
|
||||||
|
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