在Ruby中,当n是数组大小时,在Array中插入的预期数量级是多少? 感谢
答案 0 :(得分:5)
TL; DR追加(一种特殊形式的插入)项目到数组的末尾通常在O(1)时间内完成。
让我们来看看(MRI)ruby源代码,看看为什么会这样。我们从这行ruby代码开始:
a = [1,2]
Ruby准备一个数组对象,然后在this C function中初始化它。 检查参数的有效性,然后检查数组的sets the capacity of the new array to the estimated length:
ary_resize_capa(ary, len);
数组的容量是数组在从操作系统分配的内存块中可能保存的元素数。
数组的长度(数组实际保存的元素数)始终小于或等于容量。
通过设置数组的容量,ruby确保分配足够的内存来保存数组中len
个项目。
现在,让我们在数组的末尾添加一个元素:
a << 3
source of the <<
function看起来像这样:
VALUE
rb_ary_push(VALUE ary, VALUE item)
{
long idx = RARRAY_LEN(ary);
VALUE target_ary = ary_ensure_room_for_push(ary, 1);
RARRAY_PTR_USE(ary, ptr, {
RB_OBJ_WRITE(target_ary, &ptr[idx], item);
});
ARY_SET_LEN(ary, idx + 1);
return ary;
}
这段代码看起来并不太可怕。它发现新元素的索引(idx
)是数组的长度,确保数组有足够的内存来保存新元素(ary_ensure_room_for_push
),将新元素写入数组,并增加数组长度。
当数组的容量大于其长度时,不需要在ary_ensure_room_for_push
中分配更多内存,操作可以在O(1)时间内完成。
当数组的容量等于其长度时(数组中的内存量可以精确地保存它所拥有的元素数),ary_ensure_room_for_push
需要增加容量,以便可以保留另外一个元素数组。
让我们看看这是如何完成的:
static VALUE
ary_ensure_room_for_push(VALUE ary, long add_len)
{
long old_len = RARRAY_LEN(ary);
long new_len = old_len + add_len;
long capa;
// ...
rb_ary_modify(ary);
capa = ARY_CAPA(ary);
if (new_len > capa) {
ary_double_capa(ary, new_len);
}
return ary;
}
如果请求的长度超过当前容量,我们会看到ary_ensure_room_for_push
将数组容量加倍(引擎盖ary_double_capa
使用我们在数组初始化期间看到的ary_resize_capa
方法)。此代码从操作系统请求新的(更大的)内存块,并将所有数组元素复制到此新内存中。我们无法确切地说出复制操作有哪些复杂性(不要过多地考虑操作系统内部),但我们假设在最坏的情况下它是O(n)。
当新元素符合数组容量时,这会导致O(1)时间向数组添加元素;如果超出容量,则会导致O(n)。
仅供参考:将容量加倍(而不是将其精确地增加所请求的长度)是一种巧妙的技巧,可以优化多次向阵列添加元素的情况。有了这个技巧,我们大部分时间都有O(1)时间进行追加操作。仅对于每个log(n)
个追加操作,需要增加容量,从而产生O(n)运行时。
答案 1 :(得分:0)
复杂度为O(N),因为此方法在Memmove()
方法下使用rb_ary_splice()
本身就是O(N),看一下源代码:
rb_ary_insert(int argc, VALUE *argv, VALUE ary)
{
long pos;
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
rb_ary_modify_check(ary);
if (argc == 1) return ary;
pos = NUM2LONG(argv[0]);
if (pos == -1) {
pos = RARRAY_LEN(ary);
}
if (pos < 0) {
pos++;
}
rb_ary_splice(ary, pos, 0, rb_ary_new4(argc - 1, argv + 1));
return ary;
}
rb_ary_splice(VALUE ary, long beg, long len, VALUE rpl)
{
long rlen;
long olen;
if (len < 0) rb_raise(rb_eIndexError, "negative length (%ld)", len);
olen = RARRAY_LEN(ary);
if (beg < 0) {
beg += olen;
if (beg < 0) {
rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
beg - olen, -olen);
}
}
if (olen < len || olen < beg + len) {
len = olen - beg;
}
if (rpl == Qundef) {
rlen = 0;
}
else {
rpl = rb_ary_to_ary(rpl);
rlen = RARRAY_LEN(rpl);
olen = RARRAY_LEN(ary); /* ary may be resized in rpl.to_ary too */
}
if (beg >= olen) {
VALUE target_ary;
if (beg > ARY_MAX_SIZE - rlen) {
rb_raise(rb_eIndexError, "index %ld too big", beg);
}
target_ary = ary_ensure_room_for_push(ary, rlen-len); /* len is 0 or negative */
len = beg + rlen;
ary_mem_clear(ary, olen, beg - olen);
if (rlen > 0) {
ary_memcpy0(ary, beg, rlen, RARRAY_CONST_PTR(rpl), target_ary);
}
ARY_SET_LEN(ary, len);
}
else {
long alen;
if (olen - len > ARY_MAX_SIZE - rlen) {
rb_raise(rb_eIndexError, "index %ld too big", olen + rlen - len);
}
rb_ary_modify(ary);
alen = olen + rlen - len;
if (alen >= ARY_CAPA(ary)) {
ary_double_capa(ary, alen);
}
if (len != rlen) {
RARRAY_PTR_USE(ary, ptr,
MEMMOVE(ptr + beg + rlen, ptr + beg + len,
VALUE, olen - (beg + len)));
ARY_SET_LEN(ary, alen);
}
if (rlen > 0) {
MEMMOVE(RARRAY_PTR(ary) + beg, RARRAY_CONST_PTR(rpl), VALUE, rlen);
}
}
RB_GC_GUARD(rpl);
}
这里是对MEMMOVE功能的引用:
https://stuff.mit.edu/afs/sipb/contrib/linux/arch/microblaze/lib/memmove.c