現在、特定の数値に対してバイナリ検索を使用してSortedList<T,U>
います。存在しない場合は、代わりに最も近い下限のキー項目を取得します。
私がよく行っているソートされていないデータの挿入がかなり遅いことがわかりました。
で同様のことを行う方法はありますSortedDictionary
かSortedList
?
現在、特定の数値に対してバイナリ検索を使用してSortedList<T,U>
います。存在しない場合は、代わりに最も近い下限のキー項目を取得します。
私がよく行っているソートされていないデータの挿入がかなり遅いことがわかりました。
で同様のことを行う方法はありますSortedDictionary
かSortedList
?
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 つあります。最も近いノードをすばやく見つける機能がありません。しかし、ツリーでの検索のアルゴリズムはわかっており (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;
}