searching/longest-increasing-subsequence.js

  1. (function (exports) {
  2. 'use strict';
  3. exports.longestIncreasingSubsequence = (function () {
  4. /**
  5. * Find the index of the first largest element in array.
  6. * Complexity: O(N).
  7. *
  8. * @private
  9. * @param {Array} array The array in which the largest
  10. * element should be found.
  11. * @return {Number} index of the first largest element
  12. */
  13. function max(array) {
  14. if (!array || !array.length) {
  15. return -1;
  16. }
  17. var maxIdx = 0;
  18. for (var i = 1; i < array.length; i += 1) {
  19. if (array[maxIdx].distance < array[i].distance) {
  20. maxIdx = i;
  21. }
  22. }
  23. return maxIdx;
  24. }
  25. /**
  26. * Default comparison method.
  27. * @private
  28. */
  29. function asc(a, b) {
  30. return a - b;
  31. }
  32. /**
  33. * Creates directed graph from given array.
  34. * Each element's neighbours are the elements which can be
  35. * after the element in the resulting sequence.<br><br>
  36. * Complexity: O(N^2).
  37. * @private
  38. * @param {Array} array The input array.
  39. * @param {Function} cmp Comparator.
  40. * @return {Object} Graph represented with list of neighbours.
  41. */
  42. function buildDag(array, cmp) {
  43. var result = [];
  44. for (var i = 0; i < array.length; i += 1) {
  45. result[i] = [];
  46. for (var j = i + 1; j < array.length; j += 1) {
  47. if (cmp(array[i], array[j]) < 0) {
  48. result[i].push(j);
  49. }
  50. }
  51. }
  52. return result;
  53. }
  54. /**
  55. * Finds the longest increasing sub-sequence for given node.<br><br>
  56. * Complexity: O(N^N).
  57. * @private
  58. * @param {Object} dag Graph represented with list of neighbours.
  59. * @param {number} node The current node.
  60. * @return {object} The longest increasing sub-sequence for given node.
  61. */
  62. function find(dag, node) {
  63. node = node || 0;
  64. if (find.memo[node]) {
  65. return find.memo[node];
  66. }
  67. var neighbours = dag[node];
  68. var neighboursDistance = [];
  69. var maxDist;
  70. // var maxNode;
  71. var distance;
  72. var result;
  73. if (!neighbours.length) {
  74. return { distance: 1, neighbour: undefined, node: node };
  75. }
  76. for (var i = 0; i < neighbours.length; i += 1) {
  77. neighboursDistance[i] = find(dag, neighbours[i]);
  78. }
  79. maxDist = max(neighboursDistance);
  80. // maxNode = neighbours[maxDist];
  81. distance = 1 + neighboursDistance[maxDist].distance;
  82. find.memo[node] = result = {
  83. distance: distance,
  84. neighbour: neighboursDistance[maxDist],
  85. node: node
  86. };
  87. return result;
  88. }
  89. /**
  90. * Algorithm from dynamic programming. It finds the longest
  91. * sub-sequence of increasing numbers. It is not required
  92. * the numbers to be neighboring. For example for 1, 5, 2
  93. * sequence the longest sub-sequence is 1, 2.
  94. *
  95. * @example
  96. * var subsequence = require('path-to-algorithms/src/searching/'+
  97. * 'longest-increasing-subsequence').longestIncreasingSubsequence;
  98. * console.log(subsequence([1, 0, 4, 3, 5])); // 1, 4, 5
  99. *
  100. * @public
  101. * @module searching/longest-increasing-subsequence
  102. * @param {Array} array Input sequence.
  103. * @param {Function} cmp Comparator.
  104. * @return {Array} Longest increasing subsequence.
  105. */
  106. return function (array, cmp) {
  107. cmp = cmp || asc;
  108. var results = [];
  109. var dag = buildDag(array, cmp);
  110. var maxPath;
  111. find.memo = [];
  112. for (var i = 0; i < array.length; i += 1) {
  113. results.push(find(dag, i));
  114. }
  115. maxPath = results[max(results)];
  116. results = [];
  117. while (maxPath) {
  118. results.push(array[maxPath.node]);
  119. maxPath = maxPath.neighbour;
  120. }
  121. return results;
  122. };
  123. })();
  124. })(typeof window === 'undefined' ? module.exports : window);