我需要HashSet还是Set?

时间:2019-05-02 14:32:09

标签: haskell

我想初始化一组从1到n(n <20000)的所有Int。然后,我要一一删除它们,同时检查某些元素是否仍在其中,直到集合为空。

哪种数据结构最适合此任务?

2 个答案:

答案 0 :(得分:4)

使用位集(也称为Integer)。 1位表示仍在集合中的值; 0位表示一个不存在的位。例如,表示从Integer1的所有数字的n将是bit (n+1) - 2(假设您打算使用0索引,对我来说似乎很明智) ;要检查数字是否在集合中,请使用testBit;要删除号码,请使用clearBit

用于同一基础思想的另一种实现策略是使用Bool的未装箱数组,根据需要可以是可变的或不可变的。未装箱的版本会进行适当的位打包。唯一的缺点是,如果您以后需要在集合中添加比原来分配的空间更大的数字,则可能不得不调整数组的大小。

答案 1 :(得分:3)

如果您要坚持使用不变的数据结构,建议使用IntSet。正是针对此类情况进行了精心优化。 Set IntInt s的平衡二进制搜索树,它需要占用大量空间和大量时间。 HashSet IntInt的数组映射特里,它可能更快,更紧凑,但仍然很普通。 IntSet是PATRICIA树,其叶子是位集。因此,它非常紧凑(装满后,是未装箱的不可变数组的大小的两倍多),但是修改效率更高。

使用IntSetInt的所有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时)。