我想初始化一组从1到n(n <20000)的所有Int。然后,我要一一删除它们,同时检查某些元素是否仍在其中,直到集合为空。
哪种数据结构最适合此任务?
答案 0 :(得分:4)
使用位集(也称为Integer
)。 1
位表示仍在集合中的值; 0
位表示一个不存在的位。例如,表示从Integer
到1
的所有数字的n
将是bit (n+1) - 2
(假设您打算使用0索引,对我来说似乎很明智) ;要检查数字是否在集合中,请使用testBit
;要删除号码,请使用clearBit
。
用于同一基础思想的另一种实现策略是使用Bool
的未装箱数组,根据需要可以是可变的或不可变的。未装箱的版本会进行适当的位打包。唯一的缺点是,如果您以后需要在集合中添加比原来分配的空间更大的数字,则可能不得不调整数组的大小。
答案 1 :(得分:3)
如果您要坚持使用不变的数据结构,建议使用IntSet
。正是针对此类情况进行了精心优化。 Set Int
是Int
s的平衡二进制搜索树,它需要占用大量空间和大量时间。 HashSet Int
是Int
的数组映射特里,它可能更快,更紧凑,但仍然很普通。 IntSet
是PATRICIA树,其叶子是位集。因此,它非常紧凑(装满后,是未装箱的不可变数组的大小的两倍多),但是修改效率更高。
使用IntSet
至Int
的所有1
初始化n
花费了O(n)
时间。如果您只初始化一次或不时初始化一次,然后n < 20000
,那不会造成任何性能问题。但是,如果您需要经常进行初始化(尤其是如果您有时在丢弃集合之前仅删除一些元素),或者n
却要大得多(例如,数亿),并且您想要减少在初始化时,您可以使用IntSet
表示要存储的集合的 complement 。
data CompSet = CompSet
{ initialMax :: !Int
, size :: !Int
, missingElements :: !IntSet
}
一个CompSet
存储初始最大值(n
),一个IntSet
表示集合中[1..initialMax]
中的哪些元素不再 。 CompSet
的大小被初始化为initialMax
,并在O(1)
的时间内通知您集合是否为空(即size missingElements = initialMax
时)。