arr.splice(i, 1);
由于它将i后面的所有元素都移动了一次,所以在最坏情况下,这会是O(n)
吗?或者说,在链表的某些神奇操作下,时间是常数?
arr.splice(i, 1);
由于它将i后面的所有元素都移动了一次,所以在最坏情况下,这会是O(n)
吗?或者说,在链表的某些神奇操作下,时间是常数?
最坏情况应该是O(n)
(将所有n-1
个元素复制到新数组中)。
对于单个删除,链表应该是O(1)
。
对于那些感兴趣的人,我做了这个懒散制作的基准测试。(请勿在Windows XP/Vista上运行)。 从这里可以看出它看起来相当稳定(即O(1)
),所以谁知道他们在幕后做了什么使它变得非常快。请注意,无论如何,实际的splice
非常快。
在V8 shell中重新运行扩展的基准测试,表明为O(n)
。请注意,您需要巨大的数组大小才能获得可能会影响您的代码的运行时。这是预期的,因为如果您查看V8代码,它使用memmove
创建新数组。
¡嗨!
我自己做了一个实验,想分享一下我的发现。实验非常简单,我们对大小为n的数组运行100个切片操作,并计算每个切片函数所花费的平均时间。然后我们改变了n的大小,以检查其表现如何。
对于大数字,它似乎呈线性增长。
我们还使用“小”数字进行了检查(尽管它们仍然相当大):
在这种情况下,它似乎是恒定的。
如果让我选择一个选项,我会说它是O(n),因为对于大数字来说,它的行为就是这样。请记住,线性行为只显示为非常大的数字。
然而,要给出一个明确的答案很难,因为JavaScript中的数组实现非常依赖于数组的声明和操作方式。
我建议阅读这个stackoverflow讨论和这个quora讨论,以了解数组的工作原理。
我在node v10.15.3中运行它,使用的代码如下:
const f = async () => {
const n = 80000000;
const tries = 100;
const array = [];
for (let i = 0; i < n; i++) { // build initial array
array.push(i);
}
let sum = 0;
for (let i = 0; i < tries; i++) {
const index = Math.floor(Math.random() * (n));
const start = new Date();
array.splice(index, 1); // UNCOMMENT FOR OPTION A
// array.splice(index, 0, -1); // UNCOMMENT FOR OPTION B
const time = new Date().getTime() - start.getTime();
sum += time;
array.push(-2); // UNCOMMENT FOR OPTION A, to keep it of size n
// array.pop(); // UNCOMMENT FOR OPTION B, to keep it of size n
}
console.log('for an array of size', n, 'the average time of', tries, 'splices was:', sum / tries);
};
f();
我采纳了评论中的建议,并编写了一个简单的测试,用于计时对一个大小为3,000的数据集数组进行分片,其中每个数组中包含3,000个条目。测试只需将:
我预先构建了数组以保持简单。
结果:最奇怪的事情是,在增加数据集大小时,splice过程的执行时间超过1ms的次数呈线性增长趋势。
我甚至在我的机器上测试了一个大小为300,000的数据集(但SO代码段在3,000之后容易崩溃)。
我还注意到,对于给定数据集(我的情况下为30,000),需要花费超过1ms的splice()
数量是随机的。因此,我运行了1000次测试并绘制了结果数量,看起来像标准分布;这让我相信随机性只是由调度程序中断引起的。
这与我的假设和@Ivan的猜测相反,即从数组开头进行splice()
将具有O(n)
时间复杂度。
let data = []
const results = []
const dataSet = 3000
function spliceIt(i) {
data[i].splice(i, 1)
}
function test() {
for (let i=0; i < dataSet; i++) {
let start = Date.now()
spliceIt(i);
let end = Date.now()
results.push(end - start)
}
}
function setup() {
data = (new Array(dataSet)).fill().map(arr => new Array(dataSet).fill().map(el => 0))
}
setup()
test()
// console.log("data before test", data)
// console.log("data after test", data)
// console.log("all results: ", results)
console.log("results that took more than 1ms: ", results.filter(r => r >= 1))
splice()
如何可能具有O(1)
时间复杂度? - Artur Grigio