我正在学习JS算法课程。这是教员针对问题的解决方案,它已经成功运行:
function arrayPreviousLess(items) {
const lessThanList = []
for (let i = items.length - 1; i >= 0; i--) {
for (let j = i; j >= 0; j--) {
if (items[i] > items[j]) {
lessThanList.unshift(items[j])
break
} else if (j === 0) {
lessThanList.unshift(-1)
}
}
}
return lessThanList
}
console.log(arrayPreviousLess([3, 5, 2, 4, 5]))
此代码比较相邻的数组项。如果上一项小于下一项,则返回较小的项。由于错误原因,它必须返回-1,如下例所示:
输入:
[3,5,2,4,5]
输出:
[-1,3,-1,2,4]
我正在理解所有内容,除了循环中break
的目的是什么?当我删除此break
时,它的工作方式相同。
答案 0 :(得分:1)
鉴于相邻对的问题描述,您所显示的算法没有多大意义。除了break
之外,没有理由使用嵌套循环,除非目的是将每个元素与其先前元素的 all 比较,在这种情况下是正确的。无论哪种方式,unshift
都比push
慢得多,并且我认为无论采用哪种算法,都没有理由诉诸该功能,也没有看到反向外循环背后的原因。
我将使用map
为相邻对编写函数。我们可以一次迭代地完成此操作。
const arrayPreviousLess = a => a.map((e, i) => a[i-1] < e ? a[i-1] : -1);
[
[3, 5, 2, 4, 5],
[1],
[1, 2],
[2, 1],
[1, 2, 3],
[3, 2, 1],
[3, 2, 3],
[3, 1, 6, 4, 5],
].forEach(e => console.log(`[${e}] => [${arrayPreviousLess(e)}]`));
另一方面,如果问题是将每个元素与其左侧的所有元素进行比较,则我将修改讲师的解决方案,至少避免使用unshift
并从前向后遍历参数数组。 / p>
const arrayPreviousLess = a => a.map((e, i) => {
while (i--) {
if (a[i] < e) return a[i];
}
return -1;
});
[
[3, 5, 2, 4, 5],
[1],
[1, 2],
[2, 1],
[1, 2, 3],
[3, 2, 1],
[3, 2, 3],
[3, 1, 6, 4, 5],
].forEach(e => console.log(`[${e}] => [${arrayPreviousLess(e)}]`));
该算法仍然是二次方的,但是有一个使用堆栈的线性算法。对于每个元素,当堆栈不为空且当前元素小于堆栈顶部时,弹出堆栈。最终,堆栈将变空或其顶部将包含先前的较小元素。如果堆栈变空,则将元素映射到-1,否则将元素映射到先前的较小元素。最后,将当前元素推入堆栈。
const arrayPreviousLess = a => {
const stack = [];
return a.map(e => {
while (stack.length && e < stack[stack.length-1]) {
stack.pop();
}
return stack.push(e) > 1 ? stack[stack.length-2] : -1;
});
};
[
[3, 5, 2, 4, 5],
[1],
[1, 2],
[2, 1],
[1, 2, 3],
[3, 2, 1],
[3, 2, 3],
[3, 1, 6, 4, 5],
].forEach(e => console.log(`[${e}] => [${arrayPreviousLess(e)}]`));
这背后的直觉是,当前元素后面的任何大于它的先前元素都不能成为先前的最小候选。我们可以丢弃它们。本质上,我们保留了元素数量最少的候选者的历史,并在保证可以有更小的元素之前就放弃任何候选者。