在C#中,我们说我有IEnumerable<Tuple<int, string>>
它按它的int元素排序。我想从它做O(log n)查找,并确保我可以这样做,使用System.Linq:
var lookups = alreadySorted.ToDictionary(x => x.Item1, x => x.Item2);
foreach(var i in someArray) someMethod(lookups.Find(i));
但这有一个缺点:在制作IEnumerable
时必须再次对已经排序的Dictionary
进行排序,因为它无法知道我已经对它进行了排序。有没有合理的解决办法?
答案 0 :(得分:1)
如果您的目标是获取O(log n)
次查询并避免进一步排序,则应使用ToList()
而不是ToDictionary()
。
然后,您可以将BinarySearch
与自定义IComparer
一起使用,仅对Item1
的{{1}}进行比较。
答案 1 :(得分:0)
作为Damien_The_Unbeliever答案的延续,我在实用程序类中定义了以下方法,因此我不必为IComparer创建新类:
public static Tuple<int, int> IndexesOf<Et, Ct>(this IList<Et> haystack, Ct needle, Func<Et, Ct, int> func)
{
int minStart = 0;
int maxStart = haystack.Count();
int minEnd = 0;
int maxEnd = haystack.Count();
while(minStart != maxStart)
{
int i = (minStart + maxStart) / 2;
var direction = func(haystack[i], needle);
if (direction < 0)
{
minStart = i+1;
minEnd = i+1 < minEnd? minEnd: i+1;
}
else if (direction == 0)
{
maxStart = i;
minEnd = i+1 < minEnd ? minEnd : i + 1;
}
else
{
maxStart = i;
maxEnd = i;
}
}
while (minEnd != maxEnd)
{
int i = (minEnd + maxEnd) / 2;
var direction = func(haystack[i], needle);
if (direction <= 0) minEnd = i+1;
else maxEnd = i;
}
return Tuple.Create(minStart, minEnd);
}
然后像
一样使用foreach(var i in someArray)
{ someMethod(lookups[lookups.IndexesOf(i, (a, b) => a.Item1 - b).Item1]);
}
当然,查找所有匹配的索引范围而不仅仅是一场比赛的索引有点浪费。