假设我在C#中有一本字典。假设密钥是可比较的,我如何找到大于给定k的最小密钥(与字典的密钥类型相同)?但是,我想使用像SortedDictionary这样的集合来有效地完成这项工作。
显然,如果不是有效地执行它的问题,可以从任何字典开始,提取其键,然后使用具有合适谓词的First方法。但是如果有一个排序的密钥组,那么它将以线性时间(按键数)执行,应该能够在日志时间内找到密钥。
感谢。
答案 0 :(得分:5)
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];
此方法执行二分查找;因此,此方法是O(log n)操作。
编辑:如果你不能确定字典是否包含你正在寻找的密钥(你只想要下一个最大的密钥),那么仍有一种方法可以利用现有的二进制文件来自.NET的搜索方法。你说你正在寻找一个“有效”的解决方案;如果你的意思是你的时间(以及代码行),以下内容符合该标准。另一方面,如果您的内存使用或性能方面,它可能并不理想。无论如何:
List<int> keysList = new List<int>(collection.Keys);
int kIndex = keysList.BinarySearch(k);
现在,BinarySearch
会给你你想要的东西,但如果钥匙不在那里,那就有点古怪了。来自MSDN documentation的返回值如下:
item 中从零开始的索引 排序
List<T>
,如果项是 发现;否则,为负数 这是一个按位补充 下一个元素的索引 大于项或者,如果没有 更大的元素,按位补码 伯爵。
这意味着您需要添加另一行:
kIndex = kIndex >= 0 ? kIndex : ~kIndex;
答案 1 :(得分:1)
对于任何字典,您必须自己对键进行排序,然后对键进行二元搜索以找到与您的值匹配的键。
这将为您提供整个操作的时间(n * log(n))+ log(n)。
如果密钥已经排序,那么您可以将其减少到log(n),但对于大多数字典,情况并非如此。
话虽这么说,将f(n)与f((n * log(n))+ log(n)的函数进行比较并查看通常要执行此操作的密钥数量变得很简单操作以及是否更好地进行线性或二进制搜索。
话虽这么说,f(n)总是低于f((n * log(n))),所以最好只是线性搜索键。
答案 2 :(得分:0)
你确定,SortedDictionary的使用会在线性时间内执行吗?由于这是微软的一个类,我希望它们能够优化它。
我建议您确实写一些测试方法以确定。
br,马塞尔
答案 3 :(得分:0)
由于SortedDictionary实现IEnumerable,为什么不循环遍历集合并在达到大于k的第一个值时停止?除非你有一个大型集合并且你的目标更接近最终,这应该会给你合理的表现。你的字典有多大?