programming-examples/js/Algorithms/longest-increasing-subsequence.js
2019-11-15 12:59:38 +01:00

136 lines
3.7 KiB
JavaScript

(function (exports) {
'use strict';
exports.longestIncreasingSubsequence = (function () {
/**
* Find the index of the first largest element in array.
* Complexity: O(N).
*
* @private
* @param {Array} array The array in which the largest
* element should be found.
* @return {Number} index of the first largest element
*/
function max(array) {
if (!array || !array.length) {
return -1;
}
var maxIdx = 0;
for (var i = 1; i < array.length; i += 1) {
if (array[maxIdx].distance < array[i].distance) {
maxIdx = i;
}
}
return maxIdx;
}
/**
* Default comparison method.
* @private
*/
function asc(a, b) {
return a - b;
}
/**
* Creates directed graph from given array.
* Each element's neighbours are the elements which can be
* after the element in the resulting sequence.<br><br>
* Complexity: O(N^2).
* @private
* @param {Array} array The input array.
* @param {Function} cmp Comparator.
* @return {Object} Graph represented with list of neighbours.
*/
function buildDag(array, cmp) {
var result = [];
for (var i = 0; i < array.length; i += 1) {
result[i] = [];
for (var j = i + 1; j < array.length; j += 1) {
if (cmp(array[i], array[j]) < 0) {
result[i].push(j);
}
}
}
return result;
}
/**
* Finds the longest increasing sub-sequence for given node.<br><br>
* Complexity: O(N^N).
* @private
* @param {Object} dag Graph represented with list of neighbours.
* @param {number} node The current node.
* @return {object} The longest increasing sub-sequence for given node.
*/
function find(dag, node) {
node = node || 0;
if (find.memo[node]) {
return find.memo[node];
}
var neighbours = dag[node];
var neighboursDistance = [];
var maxDist;
var maxNode;
var distance;
var result;
if (!neighbours.length) {
return { distance: 1, neighbour: undefined, node: node };
}
for (var i = 0; i < neighbours.length; i += 1) {
neighboursDistance[i] = find(dag, neighbours[i]);
}
maxDist = max(neighboursDistance);
maxNode = neighbours[maxDist];
distance = 1 + neighboursDistance[maxDist].distance;
find.memo[node] = result = {
distance: distance,
neighbour: neighboursDistance[maxDist],
node: node
};
return result;
}
/**
* Algorithm from dynamic programming. It finds the longest
* sub-sequence of increasing numbers. It is not required
* the numbers to be neighboring. For example for 1, 5, 2
* sequence the longest sub-sequence is 1, 2.
*
* @example
* var subsequence = require('path-to-algorithms/src/searching/'+
* 'longest-increasing-subsequence').longestIncreasingSubsequence;
* console.log(subsequence([1, 0, 4, 3, 5])); // 1, 4, 5
*
* @public
* @module searching/longest-increasing-subsequence
* @param {Array} array Input sequence.
* @param {Function} cmp Comparator.
* @return {Array} Longest increasing subsequence.
*/
return function (array, cmp) {
cmp = cmp || asc;
var results = [];
var dag = buildDag(array, cmp);
var maxPath;
find.memo = [];
for (var i = 0; i < array.length; i += 1) {
results.push(find(dag, i));
}
maxPath = results[max(results)];
results = [];
while (maxPath) {
results.push(array[maxPath.node]);
maxPath = maxPath.neighbour;
}
return results;
};
})();
})(typeof window === 'undefined' ? module.exports : window);