我想要一个有效的数据结构,它代表0..k
范围内的一组整数,其中k
非常小,让我们说小于1024
。
我提出了一个具有以下复杂性的实现:
initialize: O(1)
size: O(1)
contains: O(1)
insert: O(1)
remove: O(1)
clear: O(1)
union: O(k)
但是,我怀疑O(1)
可能真的是O(log(k))
,因为我们必须在某处读取O(log(k))
位数。
内存要求为O(k)
。
我没有实施equals
,intersection
或difference
因为我不需要它们,但它们都是O(k)
。我的实现确实支持迭代。
这似乎可能出现在许多不同的问题中,所以我想知道我的实现是否已经完成了?
更重要的是:我们能做得更好吗?
显然更好有点主观,但到处都只有O(1)
和O(log(k))
的东西可能会很有趣。
我怀疑bitsets
在k
稍大一点时可能更实用,但我不确定这么小的k
。
这是我所拥有的伪代码。它的工作原理是分配长度为k
和k+1
的数组和地图(实现为另一个数组),但只关心数组的第一个m
元素,其中{{1} }是集合的当前大小。
m
编辑:
根据我的理解,我想像一个bitset实现看起来像:
initialize: O(k) size:O(popcount of k bits)O(1) contains: O(1) insert: O(1) remove: O(1) clear: O(k) union: O(bitwise OR of k bits)
如果我们使用1024位的bitset,我不确定这是否更好。
答案 0 :(得分:1)
渐近地,您的数据结构相当整洁(大小为O(K)
,因为您需要大约2 * k整数来存储所有内容。)
我认为常量可能有点高,内存使用率还不够理想。
内存方面,你是对的,1024位(128字节)的位组也可以正常工作。将其与您的设置2 * 1024 * 4 = 8Kb进行比较。
您担心初始化。无论如何你需要分配内存,你很有可能找到128字节已经可用(与8K相比)。您需要初始化它们,但是在可能是一个或两个SIMD指令的现代架构上(编译器会在启用优化时为您执行此操作,并指定支持它们的目标平台)。
此外,128个字节将适合2个64字节缓存行,因此您的所有数据都可能适合L1缓存。
包含在代码中被称为很多。在您的建议中,您需要执行arr[map[x]] == x
。这是两个链式查找。您有2倍的缓存未命中率(因为数据太大)。此外,它无法轻松优化(CPU需要等待第一次查找的值才能发出第二次查找)。
总而言之,除了内存之外,这两个数据结构非常相似。在实践中,我敢打赌,bit-set会明显加快,特别是如果你在编译器中启用了优化。
在任何情况下,要确定您应编写代码的基准并在预期的平台上运行它。然后交换数据结构并选择能够提供最佳结果的数据结构。
答案 1 :(得分:1)
这看起来有点hacky,但您可以将该集存储到64位整数int64[16]
或类似的32位整数int32[32]
的数组中,前4位确定哪个索引是元素属于,最后6位确定将在整数中设置哪个位。所有操作都是O(1),除了clear和union将是O(log k / 64),其中k是集合中的最大元素
首先,我们有int64[16]set
添加元素x
set[x >> 6] |= 1 << (0x3F & x) // Only consider first 6 bit
删除元素
set[x >> 6] ^= 1 << (0x3F & x)
要清除:
for int i = 0; i < 16; i++{
set[i] = 0;
}
结合两组a
和b
for int i = 0; i < 16; i++{
set[i] = a[i] | b[i];
}
根据要求,检查集合是否包含x
(set[x >> 6] & (1 << (0x3F & x))) != 0
为了跟踪集合中元素的数量,一个明显的解决方案是遍历数组中每个元素的每个位,这将是O(k)时间复杂度。
很少有解决方案来计算O(1)中的位数,就像这个How to count the number of set bits in a 32-bit integer?
此外,如果我们改为使用int8[128]
设置代替当前解决方案,这意味着我们只使用数字的前3位来确定要设置的位,我们可以使用硬编码数组来保持跟踪它的位数,例如:
int numberOfElement = 0;
int[1<<8]bitCount // Pre-populated value, so bitCount[i] will give an answer of how many bit is set in i
for int i = 0; i < 128; i++ {
set[i] = a[i] | b[i];
numberOfElement += bitCount[set[i]];
}