保存数组初始化


19

我最近读到,有可能不需要初始化数组,即可以使用它们而不必花费任何时间尝试将每个成员设置为默认值。即,您可以开始使用数组,就好像它已被默认值初始化一样,而不必初始化它。(对不起,我不记得我在哪里读到的)。

例如,为什么这可能令人惊讶:

假设您正在尝试为[ 1 n 2 ]范围内的整数的最坏情况O(1)哈希表建模 (针对每个插入/删除/搜索)。[1,n2]

您可以分配大小为位的数组,并使用各个位来表示哈希表中是否存在整数。注意:分配内存被视为O1 时间。n2O(1)

现在,如果您根本不必初始化此数组,则此哈希表上的任何说运算的序列现在都是最坏的情况On nO(n)

因此,实际上,您有一个“完美的”哈希实现,对于操作序列,它使用Θ n 2空间,但运行时间为On nΘ(n2)O(n)

通常,您会希望您的运行时至少与您的空间使用情况一样糟糕!

注意:上面的示例可能用于稀疏集或稀疏矩阵的实现,所以我想这不仅具有理论意义。

所以问题是:

如何有一个像数据结构这样的数组,使我们可以跳过初始化步骤?


@Aryabhata您提到的参考是什么?
乌里2012年

1
“使用内存”与“已分配但从未访问过的内存”不同,因此,我认为动机“悖论”并不存在。
拉斐尔

1
我认为第一段很清楚:具有默认值,而无需花费时间来用默认值填充数组。如果有人在我之前有时间写它,答案是在这里Scholar.google.co.uk/…在我的博客rgrig.blogspot.co.uk/2008/12/array
rgrig

@uli:这是一个种子问题,我很久以前就读过。
Aryabhata'3

@Raphael:第一次听到这样的消息仍然令人惊讶。大多数悖论不是:-)
Aryabhata 2012年

Answers:


15

这是一个非常通用的技巧,可用于散列以外的其他目的。下面我给出一个实现(用伪代码)。

让3个未初始化矢量PV大小的Ñ每个。我们将使用它们来执行数据结构所要求的操作。我们还保持变量p o s。具体实现如下:APVnpos

init:
  pos <- 0

set(i,x):
if not(V[i] < pos and P[V[i]] = i) 
  V[i] <- pos, P[pos] <- i, pos <- pos + 1
A[i] <- x

get(i):
if (V[i] < pos and P[V[i]] = i) 
  return A[i] 
else 
  return empty 

数组仅存储通过s e t过程传递的值。数组VP用作证书,可以判断A中的给定位置是否已初始化。AsetVPA

请注意,每时每刻都会初始化0p o s 1的元素。因此,我们可以安全地将这些值用作A中初始化值的证书。对于初始化后的A中的每个位置i,向量P中都有一个对应的元素,其值等于i。这由V [ i ]指出。因此,如果我们查看对应的元素P [ V [ i ] ],其值为iP0pos1AiAPiV[i]P[V[i]]i,我们知道已被初始化(因为P永远不会说谎,因为我们考虑的所有元素都已初始化)。同样,如果A [ i ]未初始化,则V [ i ]可能指向P0范围之外的位置。p o s 1,当我们确定它尚未初始化时,或者可能指向在该范围内的位置。但是这个特定的P [ j ]对应于A中的不同位置,因此A[i]PA[i]V[i]P0..pos1P[j]A,所以我们知道P[j]i尚未初始化。A[i]

显而易见,所有这些操作都是在恒定时间内完成的。同样,每个向量使用的空间为,变量p o s的使用空间为O 1 ,因此总计为O n O(n)O(1)posO(n)


但是在没有设置A [ i ]的情况下,等于i的可能性是否可能?P[V[i]]iA[i]
罗伯特·S·巴恩斯

是,但是pos现在小于V [i],不是吗?因为否则,它不是偶然的。由于pos高于V [i],这意味着我们已将索引V [i]上的P值专门设置为我们选择的特定值,即i。
Wolfdawn 2014年

请注意,这是在(便携式)C中无法完成的工作的经典示例
。– TLW
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.