如何使用LINQ执行合并排序?

时间:2011-10-10 19:50:23

标签: .net linq mergesort

假设您有两个IEnumerbale个对象。我们如何合并它们(在某些情况下,例如合并排序合并......)并创建一个独特的IEnumerable?我用Zip尝试了这个,但在Zip中,两个列表大小应该相等(也许你没有得到异常,但也许我们丢失了一些数据。)

另外,我通过使用Enumerable.Range(...)来尝试。选择(...)但我没有得到可接受的结果。

此外,我的问题与使用Union或this one完全不同,事实上正如我所说的那样,在合并排序中合并我喜欢保留列表顺序(实际上只是想填补第一个列表中的一些空白)。

使用for循环很容易处理它,但我看不到任何完整的linq方式。

修改

Sample input:

lst1 = {5,10,12}
lst2 = {7,9,16,20,25}

result: {5,7,9,10,12,16,20,25}

这可以通过O(n + m)中的for循环和两个指针来完成,但我正在寻找O(n+m)

中的linq解决方案

for循环解决方案:

        var lst1 = new List<int> { 5, 10, 12 };
        var lst2 = new List<int> { 7, 9, 16, 20, 25 };

        var result = new List<int>();

        int j = 0;
        for (int i = 0; i < lst1.Count; i++)
        {
            while (j < lst2.Count && lst2[j] < lst1[i])
            {
                result.Add(lst2[j]);
                j++;
            }
            result.Add(lst1[i]);
        }

        while (j < lst2.Count)
        {
            result.Add(lst2[j]);
            j++;
        }
        Console.WriteLine(string.Join(",", result.ToArray()));

3 个答案:

答案 0 :(得分:11)

LINQ中没有这样的方法。而且我认为不可能将现有的方法结合起来完全按照你想要的方式进行(如果是的话,它会过于复杂)。

但是自己实施这样的方法并不难:

static IEnumerable<T> Merge<T>(this IEnumerable<T> first,
                               IEnumerable<T> second,
                               Func<T, T, bool> predicate)
{
    // validation ommited

    using (var firstEnumerator = first.GetEnumerator())
    using (var secondEnumerator = second.GetEnumerator())
    {
        bool firstCond = firstEnumerator.MoveNext();
        bool secondCond = secondEnumerator.MoveNext();

        while (firstCond && secondCond)
        {
            if (predicate(firstEnumerator.Current,  secondEnumerator.Current))
            {
                yield return firstEnumerator.Current;
                firstCond = firstEnumerator.MoveNext();
            }
            else
            {
                yield return secondEnumerator.Current;
                secondCond = secondEnumerator.MoveNext();
            }
        }

        while (firstCond)
        {
            yield return firstEnumerator.Current;
            firstCond = firstEnumerator.MoveNext();
        }

        while (secondCond)
        {
            yield return secondEnumerator.Current;
            secondCond = secondEnumerator.MoveNext();
        }
    }
}

你可以像这样使用它:

lst1.Merge(lst2, (i, j) => i < j)

答案 1 :(得分:7)

System.Linq.Enumerable中没有合并方法。如果你想要一个,你必须写它(带有for循环和yield语句)。

与许多“仅仅通过linq”问题一样 - 您应该假设每个linq方法都有一些您可以自己编写的普通旧.NET代码支持。


这是一个未经测试的徒手非通用实现

public static IEnumerable<int> Merge
(
this IEnumerable<int> source1,
IEnumerable<int> source2
)
{
  using(Enumerator<int> e1 = source1.GetEnumerator())
  {
    bool more1 = e1.MoveNext();
    using(Enumerator<int> e2 = source2.GetEnumerator()
    {
      bool more2 = e2.MoveNext();

      while (more1 && more2)
      {
        int v1 = e1.Current;
        int v2 = e2.Current;
        if (v1 < v2)
        {
          yield return v1;
          more1 = e1.MoveNext();
        }
        else
        {
          yield return v2;
          more2 = e2.MoveNext();
        }

      }
      while (more1 && ! more2)
      {
        yield return e1.Current;
        more1 = e1.MoveNext();
      }
      while (more2 && ! more1)
      {
        yield return e2.Current;
        more2 = e2.MoveNext();
      }
    }
  }
}

答案 2 :(得分:-3)