136 lines
3.7 KiB
JavaScript
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);
|
||
|
|