我正在将一些C ++代码转换为C#,并调用std :: map :: lower_bound(k)来查找映射中的键,该键的大小等于或大于k。但是,我没有看到任何方法与.NET的SortedDictionary做同样的事情。我怀疑我可以使用SortedList实现变通方法,但遗憾的是SortedList太慢(O(n)用于插入和删除键)。我该怎么办?
注意:我发现使用的解决方法利用了我的特定场景......具体来说,我的键是一个密集的整数,从0开始,所以我使用了List< TValue>作为我的字典,其中列表索引用作键,并且搜索等于或大于k的键只能在几次循环迭代中完成。但是看到原来的问题得到解答仍然会很好。
答案 0 :(得分:2)
花了几个月的工作,但最后我可以提供至少部分解决这个问题的方法......我称之为Compact Patricia Trie,一个提供“查找下一个更大的键”操作的排序字典。
http://www.codeproject.com/KB/recipes/cptrie.aspx
这只是部分解决方案,因为只支持某些类型的密钥,即byte[]
,string
和所有原始整数类型(Int8..UInt64)。此外,字符串排序区分大小写。
答案 1 :(得分:1)
问题是字典/哈希表被设计为根据输入值到达唯一的内存位置,因此您需要一个数据结构,该结构旨在容纳与您存储的每个值相关的范围,以及同时正确更新每个间隔
我认为skip lists(或平衡二叉树)可以帮助你。虽然它们不能在O(n)中执行查找,但它们可以以对数方式进行,并且仍然比树木更快。
我知道这不是一个正确的答案,因为我不能说.NET BCL已经包含这样一个类,不幸的是你必须自己实现一个,或找到一个支持它的第三方程序集。不过,The CodeProject here似乎有一个很好的例子。
答案 2 :(得分:1)
您可以尝试我在下面编写的代码。它使用二进制搜索,因此假设列表/数组已预先排序。
public static class ListExtensions
{
public static int GetAtMostIndex<TItem, TValue>(/*this*/ IList<TItem> list, TValue value, Func<TItem, TValue, int> comparer)
{
return GetAtMostIndex(list, value, comparer, 0, list.Count);
}
public static int GetAtLeastIndex<TItem, TValue>(/*this*/ IList<TItem> list, TValue value, Func<TItem, TValue, int> comparer)
{
return GetAtLeastIndex(list, value, comparer, 0, list.Count);
}
public static int GetAtMostIndex<TItem, TValue>(/*this*/ IList<TItem> list, TValue value, Func<TItem, TValue, int> comparer, int index, int count)
{
if (count == 0)
{
return -1;
}
int startIndex = index;
int endIndex = index + count - 1;
int middleIndex = 0;
int compareResult = -1;
while (startIndex < endIndex)
{
middleIndex = (startIndex + endIndex) >> 1; // / 2
compareResult = comparer.Invoke(list[middleIndex], value);
if (compareResult > 0)
{
endIndex = middleIndex - 1;
}
else if (compareResult < 0)
{
startIndex = middleIndex + 1;
}
else
{
return middleIndex;
}
}
if (startIndex == endIndex)
{
compareResult = comparer.Invoke(list[startIndex], value);
if (compareResult <= 0)
{
return startIndex;
}
else
{
int returnIndex = startIndex - 1;
if (returnIndex < index)
{
return -1;
}
else
{
return returnIndex;
}
}
}
else
{
//todo: test
return startIndex - 1;
}
}
public static int GetAtLeastIndex<TItem, TValue>(/*this*/ IList<TItem> list, TValue value, Func<TItem, TValue, int> comparer, int index, int count)
{
if (count == 0)
{
return -1;
}
int startIndex = index;
int endIndex = index + count - 1;
int middleIndex = 0;
int compareResult = -1;
while (startIndex < endIndex)
{
middleIndex = (startIndex + endIndex) >> 1; // / 2
compareResult = comparer.Invoke(list[middleIndex], value);
if (compareResult > 0)
{
endIndex = middleIndex - 1;
}
else if (compareResult < 0)
{
startIndex = middleIndex + 1;
}
else
{
return middleIndex;
}
}
if (startIndex == endIndex)
{
compareResult = comparer.Invoke(list[startIndex], value);
if (compareResult >= 0)
{
return startIndex;
}
else
{
int returnIndex = startIndex + 1;
if (returnIndex >= index + count)
{
return -1;
}
else
{
return returnIndex;
}
}
}
else
{
return endIndex + 1;
}
}
}
答案 3 :(得分:1)
我创建了三个与B +树相关的数据结构,为任何数据类型提供此功能:BList<T>
, BDictionary<K,V>
and BMultiMap<K,V>
。这些数据结构中的每一个都提供了{C} FindLowerBound()
和FindUpperBound()
的{{1}}和lower_bound
方法。
答案 4 :(得分:0)
找到离K最近的地方:
dict.Keys.Where(i => i >= K).OrderBy(i => i).First();
或更快:
public int? GetNearestKey(dict, K)
{
int? lowerK = null;
foreach (int key in dict.Keys)
{
if (key == K)
{
lowerK = K;
break;
}
else if (key >= K && (!lowerK.HasValue || key < lowerK))
{
lowerK = key;
}
}
return lowerK;
}
答案 5 :(得分:0)
基础框架中没有二进制搜索树集合实现,因此您必须构建一个或查找实现。如您所述,SortedList在搜索方面最接近,但由于插入/删除的速度较慢(由于其底层数组实现)。
答案 6 :(得分:0)
我认为关于SortedList复杂性的问题存在错误。
SortedList 具有inserting新项目的O(log(n))摊销复杂度。如果您事先知道在最坏的情况下可以在O(Log(n))中完成容量。
答案 7 :(得分:0)
您可以使用以下扩展方法为SortedSet<T>
执行此操作:
public static class SortedSetExtensions
{
public static bool FindLowerOrEqualThan<T>(this SortedSet<T> set, T value, out T first)
{
if(set.Count == 0)
{
first = default(T);
return false;
}
var minimum = set.Min;
if(set.Comparer.Compare(minimum, value) > 0)
{
first = default(T);
return false;
}
first = set.GetViewBetween(minimum, value).Max;
return true;
}
public static bool FindGreaterOrEqualThan<T>(this SortedSet<T> set, T value, out T first)
{
if (set.Count == 0)
{
first = default(T);
return false;
}
var maximum = set.Max;
if (set.Comparer.Compare(maximum, value) < 0)
{
first = default(T);
return false;
}
first = set.GetViewBetween(value, maximum).Min;
return true;
}
}