我有一组int
值,我用以下方式填充HashSet<int>
-
var hashSet = new HashSet<int>(myIEnumerable);
假设迭代IEnumerable
为O(n)
,那么以这种方式创建HashSet<int>
的最坏情况的复杂性是什么?
答案 0 :(得分:7)
答案 1 :(得分:5)
通过在集合达到其最大大小时向同一个存储桶提供所有散列的对象,可以将最坏情况带到O(N^2)
。例如,如果传递构造为
int
s序列
x[i] = i * 17519
对于介于1和17519之间的i
,所有数字都将散列到Microsoft HashSet<int>
实现的初始存储桶中,并O(N^2)
插入:
var h = new HashSet<int>(Enumerable.Range(1, 17519).Select(i => i*17519));
设置brea kpoint,并检查调试器中的h
。查看Raw View /非公共成员/ m_buckets。观察到初始存储桶有17519个元素,而其余的17518都有零。
答案 2 :(得分:2)
使用简并哈希码(常量)的快速实验表明它是二次的。
for(int n=0;n<100;n++)
{
var start=DateTime.UtcNow;
var s=new HashSet<Dumb>(Enumerable.Range(0,n*10000).Select(_=>new Dumb()));
Console.Write(n+" ");
Console.WriteLine((int)((DateTime.UtcNow-start).TotalSeconds*10));
}
输出:
0 0
1 8
2 34
3 73
4 131
现在有些人声称你没有得到HashCode
的多次碰撞。虽然这在技术上是正确的,但对性能至关重要的不是HashCode的碰撞,而是铲斗索引的碰撞。我认为HashSet<T>
使用类似bucket = (hash&0x7FFFFFFF)%Capacity
的内容。因此,如果你添加一个整数序列,它是一个首选桶大小的倍数,它仍然会很慢。