【发布时间】:2009-12-14 06:19:09
【问题描述】:
给定 n 个相同类型的枚举,它们按升序返回不同的元素,例如:
IEnumerable<char> s1 = "adhjlstxyz";
IEnumerable<char> s2 = "bdeijmnpsz";
IEnumerable<char> s3 = "dejlnopsvw";
我想有效地找到所有可枚举元素的所有值:
IEnumerable<char> sx = Intersect(new[] { s1, s2, s3 });
Debug.Assert(sx.SequenceEqual("djs"));
这里的“高效”是指
- 输入的可枚举项每个只能枚举一次,
- 应仅在需要时检索输入枚举的元素,并且
- 算法不应递归枚举其自身的输出。
我需要一些提示如何解决。
到目前为止,这是我的(天真的)尝试:
static IEnumerable<T> Intersect<T>(IEnumerable<T>[] enums)
{
return enums[0].Intersect(
enums.Length == 2 ? enums[1] : Intersect(enums.Skip(1).ToArray()));
}
Enumerable.Intersect 将第一个可枚举对象收集到一个 HashSet 中,然后枚举第二个可枚举对象并产生所有匹配的元素。
Intersect 然后递归地将结果与下一个可枚举相交。
这显然不是很有效(它不符合约束)。而且它根本没有利用元素已排序的事实。
这是我尝试将两个枚举相交。也许它可以推广到 n 枚举?
static IEnumerable<T> Intersect<T>(IEnumerable<T> first, IEnumerable<T> second)
{
using (var left = first.GetEnumerator())
using (var right = second.GetEnumerator())
{
var leftHasNext = left.MoveNext();
var rightHasNext = right.MoveNext();
var comparer = Comparer<T>.Default;
while (leftHasNext && rightHasNext)
{
switch (Math.Sign(comparer.Compare(left.Current, right.Current)))
{
case -1:
leftHasNext = left.MoveNext();
break;
case 0:
yield return left.Current;
leftHasNext = left.MoveNext();
rightHasNext = right.MoveNext();
break;
case 1:
rightHasNext = right.MoveNext();
break;
}
}
}
}
【问题讨论】:
-
重新评论;为什么你需要将它与其他任何东西结合起来?它似乎“按原样”完成工作?
-
重新“聚合”(评论) - 不完全;如果您使用
Empty()作为种子,您的答案将始终为空...但除了初始条件之外 - 差不多!