【问题标题】:Fast way to find the first unused key in a SortedDictionary?在 SortedDictionary 中找到第一个未使用的键的快速方法?
【发布时间】:2014-12-29 12:49:07
【问题描述】:

如果我有SortedDictionary<int, object>,找到当前未使用的最低键的最快方法是什么?显然,我们可以从 0->int.MaxValue 迭代一个计数器 i 并在 !Keys.Contains(i) 时转义,但这将非常慢,除非我们很幸运并且第一个备用密钥恰好在密钥序列的早期。也许甚至一个不同的 .NET 类已经为我们做到了这一点?

【问题讨论】:

  • 您对您的字典中可能使用的人群有任何了解吗?可能有多少个键?
  • 当然 - 事实上,我很乐意采用我建议的笨重方式,或者确实跟踪备用钥匙,因为我不太可能拥有庞大的字典,而是一种聪明的方式会很好:)

标签: c# .net key sorteddictionary


【解决方案1】:

所以,如果我理解正确的话,密钥可以是从 0int.MaxValue 的任意位置。在这种情况下,您必须找到键序列中的第一个“洞”。

这应该可以有效地完成工作:

public static int GetFirstUnusedKey<TValue>(SortedDictionary<int, TValue> dict)
{
    if (dict.Comparer != Comparer<int>.Default)
        throw new NotSupportedException("Unsupported comparer");

    using (var enumerator = dict.GetEnumerator())
    {
        if (!enumerator.MoveNext())
            return 0;

        var nextKeyInSequence = enumerator.Current.Key + 1;

        if (nextKeyInSequence < 1)
            throw new InvalidOperationException("The dictionary contains keys less than 0");

        if (nextKeyInSequence != 1)
            return 0;

        while (enumerator.MoveNext())
        {
            var key = enumerator.Current.Key;
            if (key > nextKeyInSequence)
                return nextKeyInSequence;

            ++nextKeyInSequence;
        }

        return nextKeyInSequence;
    }
}

我添加了一些检查以确保先决条件有效。

【讨论】:

    【解决方案2】:

    为什么不对键进行二分搜索?

    您可以使用 ElementAt() 方法来识别索引处的键值。如果键值大于索引,则在左侧子字典中搜索或选择右侧并继续下去,直到找到您观察到索引与索引值之间的第一个差异的索引。

    【讨论】:

    • 不。 ElementAt 将在每次调用@DrKoch 时枚举字典。这使得这个解决方案更像 O(n²)。如果SortedDictionary 实现IList&lt;T&gt;,它将起作用。
    • 嗯。好点@Lucas,但如果我们寻找以 intmax/2 开头的键,然后向左走,如果左半部分已满,则可能再向右走?
    • @Rob 这里的问题是SortedDictionary&lt;TKey, TValue&gt;.KeyCollection 类没有实现IList&lt;TKey&gt;,只有ICollection&lt;TKey&gt; 并且没有被索引。
    • @DrKoch 我可以这样做,但是我会失去使用字典的好处。我的应用程序需要 a) 使用不会更改的键跟踪对象,并且 b) 能够通过其键 快速 检索对象。密钥溢出问题不太可能发生但需要注意,以防万一
    【解决方案3】:

    我并没有为这种方法声称任何形式的性能奖杯,但我认为它在某种程度上有助于实现您的目标:

    var sd = new SortedDictionary<int, object>();
    
    sd.Add(1, 1);
    sd.Add(2, 1);
    sd.Add(4, 1);
    sd.Add(5, 1);
    
    var e = Enumerable.Range(1, 5).Except(sd.Keys).First();
    

    e = 3 在这种情况下,如预期的那样。不过,我希望有更好的解决方案。

    【讨论】:

    • 不错。我认为这会非常有效。
    • 我相信这对于一本小字典来说会很好。我不知道它会如何扩展。
    【解决方案4】:

    不要从 0 开始搜索未使用的密钥。

    而是迭代使用的键,从最低的开始(它们已排序)。

    第一个是 >0,然后 0 是您未使用的最低值。

    或者两个键之间有间隔,那么lower+1就是你搜索的。

    【讨论】:

    • 我猜这是卢卡斯回答的散文版本;)
    • 大声笑。 DrKoch - 当然,但对于几乎“已满”的大型词典仍然存在问题。主键溢出时数据库怎么办?
    • 好吧,如果你经常这样做,你可以维护一个变量“LastGapFoundAtKey”,下次再从那里开始。
    • 数据库首先不会使用SortedDictionary ;) 如果您的字典已满,您应该使用不同的数据结构。
    • 它不会经常发生,所以事实上大多数方法都可以,但我只是想知道是否有一个聪明的 O(log n) 或更好的方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-04
    • 1970-01-01
    • 2011-11-29
    • 2021-06-07
    • 1970-01-01
    相关资源
    最近更新 更多