目前我在SortedList<T,U>
上使用二进制搜索来获取特定数字,如果它不存在,我会获得最接近的下限键项。
我在inserting unsorted data看到它很慢,我做了很多。
有没有办法与SortedDictionary
做类似的事情,或者我应该坚持使用SortedList
?
答案 0 :(得分:10)
SortedList<K, V>
非常慢,因为每次添加新元素时,它会在内部数组中移动<=N
个元素。添加的复杂性为O(N)
。然而,它支持二进制搜索,允许在O(log N)
中找到确切的元素或其邻居。
平衡二叉树是解决问题的最佳数据结构。 您将能够以对数复杂度执行以下操作:
O(log N)
O(N)
与SortedList<K, V>
中添加项目
O(log N)
O(log N)
在二叉树中查找元素或其最近的下界很简单:
有很多文章描述了如何实现二叉树。不过,我将使用一种黑客重用.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;
}