我有几个JavaScript数组,每个数组都包含一个指向对象的指针列表。当一个对象满足某个条件时,它的指针必须从当前包含的数组中删除并放入另一个数组中。
我当前(天真)的解决方案是splice
退出数组元素,concat
将它们添加到他们输入的数组中。这是一种缓慢的方法,似乎随着时间的推移会破坏记忆。
任何人都可以通过更好的方式提供建议(一般或特定于JS)吗?
演示代码:
// Definitions
TestObject = function() {
this.shouldSwitch = function() {
return(Math.random() > 0.9);
}
}
A = [];
B = [];
while(A.length < 500) {
A.push(new TestObject());
}
// Transfer loop
doTransfers = function() {
var A_pending = [];
var B_pending = [];
for(var i = 0; i < A.length; i++) {
if(A[i].shouldSwitch()) {
B_pending.push(A[i]);
A.splice(i,1);
i--;
}
}
for(var i = 0; i < B.length; i++) {
if(B[i].shouldSwitch()) {
A_pending.push(B[i]);
B.splice(i,1);
i--;
}
}
A = A.concat(A_pending);
B = B.concat(B_pending);
}
setInterval(doTransfers,10);
谢谢!
答案 0 :(得分:1)
对于与此问题无关的语言解决方案,当您将元素从一个连续序列(数组)传输到另一个时,它不会将元素附加到新数组的后面&# 39;它将成为瓶颈(恒定的时间复杂度),它将从现有容器的中间移除元素(线性时间复杂度)。
因此,您可以获得的最大好处是使用仍然使用缓存友好的连续数组表示的常量操作替换从数组中间删除的线性时间操作。
最简单的方法之一是简单地创建两个新数组而不是一个:一个新数组用于追加要保留的元素,一个新数组用于追加要传输的元素。完成后,您可以使用旧阵列替换要保留(而不是传输)的新元素数组。
在这种情况下,我们将容器中间的线性时间删除与分摊的常量时间插入交换到新容器的后面。虽然插入到容器的末端仍然具有重新分配的O(N)的最坏情况复杂性,但是它很少发生并且通常远远好于支付每次平均复杂度为O(N)的操作。通过不断从中间移除来转移单个元素。
解决这个问题的另一种方法可以更高效,特别是对于某些情况,比如非常小的数组,因为它只创建了1个新数组,这是:
...当你转移一个元素时,首先将它的副本(可能只是一个浅拷贝)附加到新容器。然后使用旧容器背面的元素覆盖旧容器中该索引处的元素。现在只需弹出旧容器背面的元素即可。所以我们有一个推,一个任务,一个流行。
在这种情况下,我们通过单个赋值(存储/移动指令)和容器背面的常量弹出(通常是基本算术)从容器中间交换线性时间删除)。如果旧数组中元素的顺序可以稍微改变,这可以非常好地工作,并且通常是一种被忽视的解决方案,用于将从数组中间的线性时间移除到具有恒定时间复杂度的数组中。阵列的后面。
答案 1 :(得分:0)
splice
对循环中的性能非常有害。但是你似乎不需要输入数组上的突变 - 你正在创建新的突变并覆盖以前的值。
只做
function doTransfers() {
var A_pending = [];
var B2_pending = [];
for (var i = 0; i < A.length; i++) {
if (A[i].shouldSwitch())
B_pending.push(A[i]);
else
A_pending.push(A[i]);
}
var B1_pending = [];
for (var i = 0; i < B.length; i++) {
if (B[i].shouldSwitch())
A_pending.push(B[i]);
else
B1_pending.push(B[i]);
}
A = A_pending;
B = B1_pending.concat(B2_pending);
}