【发布时间】:2012-07-26 08:10:52
【问题描述】:
目前,我在 SortedList<T,U> 上使用二进制搜索来查找特定数字,如果它不存在,我将获得最接近的下限键项。
我看到 inserting unsorted data 的速度相当慢,我经常这样做。
有没有办法用SortedDictionary 做类似的事情,还是我应该坚持我的SortedList?
【问题讨论】:
标签: c# .net performance collections
目前,我在 SortedList<T,U> 上使用二进制搜索来查找特定数字,如果它不存在,我将获得最接近的下限键项。
我看到 inserting unsorted data 的速度相当慢,我经常这样做。
有没有办法用SortedDictionary 做类似的事情,还是我应该坚持我的SortedList?
【问题讨论】:
标签: c# .net performance collections
SortedList<K, V> 在插入数据时非常慢,因为每次添加新元素时它都会移动内部数组中的<=N 元素。加法的复杂度为O(N)。不过它支持二分查找,可以在O(log N) 中找到准确的元素或其邻居。
平衡二叉树是解决问题的最佳数据结构。 您将能够执行以下具有对数复杂度的操作:
O(log N) 与O(N) 在SortedList<K, V> 中添加项目
O(log N) 中的项目
O(log N)中搜索项目或其最近的项目
在二叉树中寻找元素或其最近的下界很简单:
有很多文章描述了如何实现二叉树。不过,我将使用一种 hack 重用 .NET Framework 集合:)
现在,我将向您展示SortedSet<T>,它本身就是红黑树。它有一个缺点,它无法快速找到最近的节点。但是我们知道在树中搜索的算法(在 1. 中有描述),它是在SortedSet<T>.Contains 方法中实现的(在底部反编译*)。现在我们可以使用我们的自定义比较器在遍历期间捕获从根到最后访问的节点的所有节点。之后我们可以使用上面的算法找到最近的下界节点:
public class LowerBoundSortedSet<T> : SortedSet<T> {
private ComparerDecorator<T> _comparerDecorator;
private class ComparerDecorator<T> : IComparer<T> {
private IComparer<T> _comparer;
public T LowerBound { get; private set; }
private bool _reset = true;
public void Reset()
{
_reset = true;
}
public ComparerDecorator(IComparer<T> comparer)
{
_comparer = comparer;
}
public int Compare(T x, T y)
{
int num = _comparer.Compare(x, y);
if (_reset)
{
LowerBound = y;
}
if (num >= 0)
{
LowerBound = y;
_reset = false;
}
return num;
}
}
public LowerBoundSortedSet()
: this(Comparer<T>.Default) {}
public LowerBoundSortedSet(IComparer<T> comparer)
: base(new ComparerDecorator<T>(comparer)) {
_comparerDecorator = (ComparerDecorator<T>)this.Comparer;
}
public T FindLowerBound(T key)
{
_comparerDecorator.Reset();
this.Contains<T>(key);
return _comparerDecorator.LowerBound;
}
}
你看到找到最近的节点并不比通常的搜索多,即O(log N)。因此,这是解决您问题的最快方法。这个集合在寻找最近的时候和SortedList<K, V>一样快,另外和SortedSet<T>一样快。
SortedDictionary<K, V> 呢?它与SortedSet<T> 几乎相同,除了一件事:每个键都有一个值。我希望你也能对SortedDictionary<K, V> 做同样的事情。
*反编译的SortedSet<T>.Contains方法:
public virtual bool Contains(T item)
{
return this.FindNode(item) != null;
}
internal virtual SortedSet<T>.Node FindNode(T item)
{
for (SortedSet<T>.Node node = this.root; node != null; {
int num;
node = num < 0 ? node.Left : node.Right;
}
)
{
num = this.comparer.Compare(item, node.Item);
if (num == 0)
return node;
}
return (SortedSet<T>.Node) null;
}
【讨论】: