【问题标题】:How do I find next largest key in a collection?如何找到集合中的下一个最大键?
【发布时间】:2009-12-28 22:36:11
【问题描述】:

假设我有一本 C# 字典。假设键是可比较的,我如何找到大于给定 k 的最小键(与字典键的类型相同)?但是,我想使用 SortedDictionary 之类的集合有效地做到这一点。

显然,如果不是高效地执行此操作的问题,则可以从任何字典开始,提取其键,然后将 First 方法与合适的谓词一起使用。但这将在线性时间(以键的数量)执行,如果一个人有一组排序的键,则应该能够在日志时间内找到键。

谢谢。

【问题讨论】:

    标签: c# collections dictionary


    【解决方案1】:

    SortedList<TKey, TValue> 类实现了IDictionary<TKey, TValue> 并有一个IndexOfKey 方法;我想这就是你想要的:

    // I'm just going to pretend your keys are ints
    var collection = new SortedList<int, string>();
    
    // populate collection with whatever
    
    int k = GetK(); // or whatever
    
    int kIndex = collection.IndexOfKey(k);
    
    int? smallestKeyGreaterThanK = null;
    if (collection.Count > kIndex + 1)
        smallestKeyGreaterThanK = collection.Keys[kIndex + 1];
    

    根据MSDN documentation

    此方法执行二分查找;因此,此方法是 O(log n) 操作。

    编辑:如果您不能确定字典是否包含您要查找的键(您只需要下一个最大的键),仍然有一种方法可以利用现有的二进制文件为您的目的从 .NET 搜索方法。您说您正在寻找“有效”的解决方案;如果您的意思是您的时间(以及代码行数),则以下内容符合该标准。另一方面,如果您的意思是内存使用或性能,它可能并不理想。无论如何:

    List<int> keysList = new List<int>(collection.Keys);
    int kIndex = keysList.BinarySearch(k);
    

    现在,BinarySearch 会为您提供您正在寻找的东西,但如果钥匙不在那里,那就有点古怪了。来自MSDN documentation的返回值如下:

    item 中的从零开始的索引 已排序 List&lt;T&gt;,如果 item 是 成立;否则为负数 这是的按位补码 下一个元素的索引 大于 item 或者,如果没有 较大的元素,按位补码 计数。

    这意味着您需要添加另一行:

    kIndex = kIndex >= 0 ? kIndex : ~kIndex;
    

    【讨论】:

    • 谢谢。不幸的是,就我而言,我不能保证集合包含 k 作为键。事实上,鉴于您的回答,我现在怀疑没有办法避免在键上手动编码二进制搜索(在这种情况下可能更好地称为二分搜索)。
    • @banbh:可能。您可以作弊并使用List&lt;T&gt; 类提供的BinarySearch 方法(请参阅我的编辑);但这需要分配更多您实际上不需要分配的内存。不过,如果你真的反对编写自己的二分搜索,它会起作用。
    • 如果键来自未排序的字典,请不要忘记在二进制搜索之前对该列表进行排序。
    • @Aaron:我最初的建议是使用SortedList&lt;TKey, TValue&gt;,在这种情况下,密钥会提前排序。但总的来说,你指出这一点是对的。
    • 如果不能保证该值存在,则不一定有任何理由使用SortedList。特别是对于大的 N,通常使用未排序的集合并在需要时执行手动排序会更快。当然,如果你无论如何都要复制整个字典,那么你已经有一个 O(N) 操作,所以二进制搜索有点多余,只需做一个线性搜索。 ;)
    【解决方案2】:

    对于任何字典,您都必须自己对键进行排序,然后对键进行二进制搜索以找到与您的值匹配的那个。

    这将为您提供整个操作的 (n * log(n)) + log(n) 时间。

    如果键已经排序,那么您可以将其减少到 log(n),但对于大多数字典,情况并非如此。

    话虽如此,比较 f(n) 与 f((n * log(n)) + log(n)) 的函数并查看您通常需要多少键来执行此操作变得很简单操作,以及是否更好地进行线性搜索或二进制搜索。

    话虽如此,f(n) 总是低于 f((n * log(n))),所以最好只线性搜索键。

    【讨论】:

    • 对,这就是我想要达到的目的!假设我从一个 SortedDictionary 开始,那么(我希望)找到我在原始问题中描述的键应该很简单。但是,浏览 MSDN 帮助文件,似乎我需要重新发明轮子(如您在上面描述的),这似乎很愚蠢。
    • 看起来对于任何 n,n 都将小于 n*log(n) + log(n)。为什么要比较绘制的值?如果我们要遍历整个集合,则不需要排序字典;一个简单的列表将始终在 O(n) 时间内完成此操作。
    • @Tarydon 该声明更多地向 OP 表明如何确定最佳性能影响是什么。但是,我已经更改了答案以给出更明确的答案,更明确。
    【解决方案3】:

    您确定使用 SortedDictionary 会在线性时间内执行吗?由于这是微软的课程,我希望他们对其进行优化。

    我建议你实际上写一些测试方法来确定。

    br,马塞尔

    【讨论】:

      【解决方案4】:

      既然 SortedDictionary 实现了 IEnumerable,为什么不循环遍历集合并在遇到第一个大于 k 的值时停止?除非您有大量集合并且您的目标更接近结尾,否则这应该会给您带来合理的性能。你的字典有多大?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多