Javascript 的最长递增子序列问题

Longest Increasing Subsequence Problem with Javascript

我为最长递增子序列生成了这段代码:

var lengthOfLIS = function(nums) {
  if (nums.length === 0) return 0;
  let result = 1;
  const LISEndAt = new Array(nums.length).fill(1);

  for (let i = 1; i < nums.length; i++) {
    LISEndAt[i] = Math.max(1,
      ...LISEndAt.slice(0, i).map((item, j) => {
        return nums[i] > nums[j] ? item + 1 : 0
      }));

    result = Math.max(LISEndAt[i], result);
  }
  
  return result;
}

let nums = [7,7,7,7,7,7,7];

console.log(lengthOfLIS(nums));

谁能帮我理解这一行:

LISEndAt[i] = Math.max(1,...LISEndAt.slice(0, i).map((item, j)

three dots 的意义是什么,我们知道 map 生成键值对,mapslice 一起做什么?

澄清

在我解释您所询问的行的作用之前,有几件事需要弄清楚。

...

...spread syntax 扩展给定的可迭代对象(数组索引、对象 key-value 对等)。这是一个示例,其中现有数组用于创建包含更多元素的新数组:

const first = [1, 2, 3];
const second = [...first, 4, 5, 6];
console.log(second);
/* returns [1, 2, 3, 4, 5, 6] */

在您的示例中,它用于向 Math.max() 函数提供多个参数。这是一个例子:

const candidates = [1, 5, 6, 2, 4];
const largest = Math.max(...candidates);
console.log(largest);
/* returns 6 */

数组#map

Array#map 不会创建 key/value 对。相反,它通过对原始数组中的每个元素执行操作来创建一个新数组。这是一个通过将原始数组中的每个项目加倍来创建新数组的示例:

const original = [1, 2, 3];
const doubled = original.map(i => i * 2);
console.log(doubled);
/* returns [2, 4, 6] */

您还可以访问原始数组中每一项的索引:

const alphabet = ['A', 'B', 'C'];
const positions = alphabet.map((letter, index) => index);
console.log(positions);
/* returns [0, 1, 2] */

数组#slice

Array#slice 创建一个新数组,该数组是从提供的起始索引到(但不包括)提供的结束索引的原始数组的子集。这是一个从现有数组的前三项创建新数组的示例:

const superset = [1, 2, 3, 4, 5, 6];
const subset = superset.slice(0, 3);
console.log(subset);
/* returns [1, 2, 3] */

你的问题

考虑到所有这些,让我们看一下包含您的行的循环:

const nums = [10, 9, 2, 5, 3, 7, 101, 18];
const LISEndAt = new Array(nums.length).fill(1);
/* [1, 1, 1, 1, 1, 1, 1, 1] */

for (let i = 1; i < nums.length; i++) {
    LISEndAt[i] = Math.max(1,
        ...LISEndAt.slice(0, i).map((item, j) => {
            return nums[i] > nums[j] ? item + 1 : 0;
        }));
}

LISEndAt 随 for-loop 的每次迭代而更新。每次,下一个索引计算为:

  • 1 中任意元素的最大值:
  • LISEndAt 的子集,从 0 到 for-loop 的当前迭代,修改者为:
  • map 中的一些比较,其中 returns 是:
  • 正在修改的当前索引处的项目加一或:
  • 零。

如果我们在您的循环中添加一些日志记录,您可以看到这会按顺序发生:

const nums = [10, 9, 2, 5, 3, 7, 101, 18];
const LISEndAt = new Array(nums.length).fill(1);
console.log(`original: ${JSON.stringify(LISEndAt)}`);

for (let i = 1; i < nums.length; i++) {
  const subset = LISEndAt.slice(0, i);
  console.log(`sliced: [${subset.join(', ')}]`);
  const someArray = subset.map((item, j) => {
    console.log(`comparing ${nums[i]} > ${nums[j]} ? ${item + 1} : 0`);
    return nums[i] > nums[j] ? item + 1 : 0;
  });
  console.log(`taking max of 1, ${JSON.stringify(...someArray)}`);
  LISEndAt[i] = Math.max(1, ...someArray);
  console.log(`iteration ${i}: ${JSON.stringify(LISEndAt)}`);
}