C# 快速查找SortedDictionary中第一个未使用的键的方法?

C# 快速查找SortedDictionary中第一个未使用的键的方法?,c#,.net,key,sorteddictionary,C#,.net,Key,Sorteddictionary,如果我有一个SortedDictionary,找到当前未使用的最低键的最快方法是什么?显然,我们可以从0->int.MaxValue迭代计数器i,并在时转义!Keys.Contains(i),但这将非常缓慢,除非我们运气好,并且第一个备用密钥恰好在密钥序列的早期。也许甚至一个不同的.NET类已经在为我们做这件事了?我并没有因为这种方法而要求任何形式的性能奖杯,但我认为它在某种程度上有助于实现您的目标: var sd = new SortedDictionary<int, object>

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

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

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();
var sd=new SortedDictionary();
sd.添加(1,1);
sd.添加(2,1);
sd.添加(4,1);
sd.添加(5,1);
var e=可枚举的.Range(1,5).Except(sd.Keys).First();

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

因此,如果我理解正确,键可以在
0
int.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;
    }
}
public static int GetFirstUnusedKey(SortedDictionary dict)
{
if(dict.Comparer!=Comparer.Default)
抛出新的NotSupportedException(“不支持的比较器”);
使用(var枚举器=dict.GetEnumerator())
{
如果(!enumerator.MoveNext())
返回0;
var nextKeyInSequence=enumerator.Current.Key+1;
if(nextKeyInSequence<1)
抛出新的InvalidOperationException(“字典包含小于0的键”);
if(nextKeyInSequence!=1)
返回0;
while(枚举数.MoveNext())
{
var key=enumerator.Current.key;
如果(键>下一个键序列)
返回nextKeyInSequence;
++nextKeyInSequence;
}
返回nextKeyInSequence;
}
}

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

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

相反,迭代使用的键,从最低点开始(它们已排序)

第一个值大于0,然后0是最低未使用值


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

为什么不对键进行二进制搜索


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

你知道你字典里可能有多少人吗?可能会有多少把钥匙?当然-事实上,我很乐意按照我建议的笨重的方式去做,或者确实会跟踪备用钥匙,因为我不太可能有庞大的字典,但聪明的方式会很好:)很好。我想那会很有效率的。我相信用一本小字典就可以了。我不知道它将如何扩展。我猜这是卢卡斯答案的散文版;)Lol.DrKoch-当然可以,但对于几乎“满”的大型词典来说仍然是个问题。当主键溢出时,数据库会做什么?如果您经常这样做,您可以维护一个变量“LastGapFoundAtKey”,并在下次从该变量开始。数据库首先不会使用
SortedDictionary
)如果你的字典很满,你应该使用不同的数据结构。这种情况不会经常发生,所以事实上任何方法都可以,但我只是想知道是否有一种聪明的O(logn)或更好的方法
ElementAt
将在每次调用@DrKoch时枚举字典。这使得此解决方案更像O(n²)。如果
SortedDictionary
实现
IList
.Hmmm,它将起作用。很好@Lucas,但是如果我们查找的键从intmax/2开始,然后向左搜索,如果左半部分已满,可能再向右搜索,那么二进制搜索可能会有所帮助。@Rob这里的问题是类
SortedDictionary.KeyCollection
没有实现
IList
,只有
ICollection
而且没有索引。@科赫博士,我可以这样做,但那样我就失去了使用字典的好处。我的应用程序需要a)使用一个不变的键跟踪对象,b)能够快速地通过其键检索对象。密钥溢出问题不太可能发生,但需要注意,以防万一