给定填充的SortedList<DateTime, double>
。对于给定的低和高日期时间间隔,我想得到所有键(或它们的索引范围,应该是一个封闭的int间隔(错过了什么?))。
注意:不需要低值和高值实际上在SortedList中。
如果有人在没有SortedList的情况下更好地了解如何做到这一点,那么我想要做的范围有点广泛,我发现SortedList可能是合适的:
答案 0 :(得分:4)
我想知道为什么SortedList<TKey, TValue>
在已经按键排序时没有提供BinarySearch
。它也使用方法本身(例如IndexOf
),但使用的数组是私有字段。
所以我试图为此创建一个扩展方法,看看:
public static class SortedListExtensions
{
public static int BinarySearch<TKey, TValue>(this SortedList<TKey, TValue> sortedList, TKey keyToFind, IComparer<TKey> comparer = null)
{
// need to create an array because SortedList.keys is a private array
var keys = sortedList.Keys;
TKey[] keyArray = new TKey[keys.Count];
for (int i = 0; i < keyArray.Length; i++)
keyArray[i] = keys[i];
if(comparer == null) comparer = Comparer<TKey>.Default;
int index = Array.BinarySearch<TKey>(keyArray, keyToFind, comparer);
return index;
}
public static IEnumerable<TKey> GetKeyRangeBetween<TKey, TValue>(this SortedList<TKey, TValue> sortedList, TKey low, TKey high, IComparer<TKey> comparer = null)
{
int lowIndex = sortedList.BinarySearch(low, comparer);
if (lowIndex < 0)
{
// list doesn't contain the key, find nearest behind
// If not found, BinarySearch returns the complement of the index
lowIndex = ~lowIndex;
}
int highIndex = sortedList.BinarySearch(high, comparer);
if (highIndex < 0)
{
// list doesn't contain the key, find nearest before
// If not found, BinarySearch returns the complement of the index
highIndex = ~highIndex - 1;
}
var keys = sortedList.Keys;
for (int i = lowIndex; i < highIndex; i++)
{
yield return keys[i];
}
}
}
创建示例SortedList
:
DateTime start = DateTime.Today.AddDays(-50);
var sortedList = new SortedList<DateTime, string>();
for(int i = 0; i < 50; i+=2)
{
var dt = start.AddDays(i);
sortedList.Add(dt, string.Format("Date #{0}: {1}", i, dt.ToShortDateString()));
}
DateTime low = start.AddDays(1); // is not in the SortedList which contains only every second day
DateTime high = start.AddDays(10);
现在您可以使用上面的扩展方法来获取低键和高键之间的键范围:
IEnumerable<DateTime> dateRange = sortedList.GetKeyRangeBetween(low, high).ToList();
结果:
04/04/2014
04/06/2014
04/08/2014
04/10/2014
请注意,这是从头开始构建的,并没有经过实际测试,但无论如何它都可以给你一个想法。
答案 1 :(得分:3)
在列表排序后,您可以使用binary search来查找间隔的端点。最差情况表现为O(log n)。
答案 2 :(得分:2)
您可以通过在Keys
上运行适当的二进制搜索两次来解决问题,以找到绑定Keys
集合中感兴趣范围的索引。
由于IList<T>
不提供二进制搜索工具,您需要自己编写。幸运的是,还可以选择从How to perform a binary search on IList窃取现成的实现。
这是一个找到下限的改编版本:
public static int LowerBound<T>(this IList<T> list, T value, IComparer<T> comparer = null)
{
if (list == null)
throw new ArgumentNullException("list");
comparer = comparer ?? Comparer<T>.Default;
int lower = 0, upper = list.Count - 1;
while (lower <= upper)
{
int middle = lower + (upper - lower) / 2;
int comparisonResult = comparer.Compare(value, list[middle]);
// slightly adapted here
if (comparisonResult <= 0)
upper = middle - 1;
else
lower = middle + 1;
}
return lower;
}
要实施UpperBound
,只需更改
if (comparisonResult <= 0)
到
if (comparisonResult < 0)
现在这样做是微不足道的:
var low = set.Keys.LowerBound(value);
var high = set.Keys.UpperBound(value);
// These extra comparisons are required because the adapted binary search
// does not tell us if it actually found the needle. They could be rolled
// into the methods themselves, but this would require another out parameter.
if (set.Keys[low] != value) ++low;
if (set.Keys[high] != value) --high;
if (low <= high) /* remove keys in the range [low, high] */