我可以从排序的IEnumerable创建一个字典而不求助吗?

时间:2017-09-05 12:08:24

标签: c# algorithm linq optimization

在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进行排序,因为它无法知道我已经对它进行了排序。有没有合理的解决办法?

编辑:我被告知Dictionary不是用于传统的二进制搜索,而是用于hashmapping,所以这个例子应该使用System.Collections.Generic.SortedSet而不是

2 个答案:

答案 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]);
}

当然,查找所有匹配的索引范围而不仅仅是一场比赛的索引有点浪费。