什么。净词典的支持"找到最近的关键的"行动?
-
18-09-2019 - |
题
我将一些C++码C#呼吁std::地图::lower_bound(k)找到一个进入的地图,其关键是等于或大于k.然而,我看不到任何方式做同样的事情。净的SortedDictionary.我怀疑我能实现一种解决方法使用SortedList,但不幸的是SortedList太慢(O(n)插入和删除键)。我能做些什么?
注:我找到一个解决方法使用,利用我的特定场景...具体地说,我的钥匙是一个人口密集的整数开始超过0,所以我用一个列表<TValue> 因为我的字典的名单索引服务作为关键,并且寻找一个关键等于或大于k可以在仅仅几个回路迭代。但是,它仍然会很高兴见到原来的问题的答案。
解决方案 4
我创建了三个数据结构有关的B+树木提供这种功能的任何数据类型: BList<T>
, BDictionary<K,V>
和 BMultiMap<K,V>
.每一个这些数据结构的提供 FindLowerBound()
和 FindUpperBound()
方法工作像C++'s lower_bound
和 upper_bound
.
其他提示
花了工作了几个月,但最后我能提供至少部分解决了这个问题......我把它称为紧凑型帕特里夏特里,一个排序的字典,提供了一个“查找下一个较大的键”操作。
http://www.codeproject.com/KB/recipes/cptrie.aspx
这只是一个部分解决方案,因为只有某些类型的密钥被支持,即byte[]
,string
,以及所有的原始整数类型(Int8..UInt64)。此外,字符串排序是大小写敏感的。
的问题是,一个字典/哈希表被设计为以基于输入值的唯一存储器位置到达,所以需要被设计,以容纳相关的每次存储值的范围内的数据结构,和在同一时间正确地更新每个间隔
我觉得跳表(或平衡二叉树)可以帮助你。虽然无法在O(N)中执行查找,就可以做的对数,并且仍然快于树木。
我知道这是不是一个正确的答案,因为我不能说了.NET基础类库已经包含了这样的类,你会很遗憾不得不实现一个自己,或者找出支持它给你一个第三方组件。似乎是一个很好的例子,在上 CodeProject上这里,虽然。
您可以尝试下面我写的代码。使用它的二进制搜索,因此假设该列表/阵列是预先排序。
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;
}
}
}
找到最接近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;
}
有没有在基本框架二叉搜索树集合实现,所以你要么必须建立一个或找到一个实现。如你所指出的,排序列表最接近在搜索方面,而且是慢的(由于其底层数组实现),用于插入/缺失。
我认为有一个错误有关的问题 SortedList 复杂性。
SortedList 已O(日志(n))分摊的复杂性 插入 新的项目。如果你事先知道的能力,这是可以做到在O(日志(n))中最糟糕的情况。
可以用下面的扩展方法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;
}
}