用于合并排序的IEnumerable <t> </t>的最有效算法

时间:2010-05-04 16:15:33

标签: c# linq performance algorithm optimization

我有几个我要合并的排序的可枚举序列。这些列表被操作为IEnumerable已经排序。由于输入列表已排序,因此应该可以在一次旅行中合并它们,而无需重新排序任何内容。

我想保留推迟的执行行为。

我试着编写一个天真的算法来做到这一点(见下文)。但是,它看起来很丑陋,我确信它可以进行优化。它可能存在一种更具学术性的算法......

IEnumerable<T> MergeOrderedLists<T, TOrder>(IEnumerable<IEnumerable<T>> orderedlists, 
                                            Func<T, TOrder> orderBy)
{
    var enumerators = orderedlists.ToDictionary(l => l.GetEnumerator(), l => default(T));
    IEnumerator<T> tag = null;

    var firstRun = true;
    while (true)
    {
        var toRemove = new List<IEnumerator<T>>();
        var toAdd = new List<KeyValuePair<IEnumerator<T>, T>>();
        foreach (var pair in enumerators.Where(pair => firstRun || tag == pair.Key))
        {
            if (pair.Key.MoveNext())
                toAdd.Add(pair);
            else
                toRemove.Add(pair.Key);
        }

        foreach (var enumerator in toRemove)
            enumerators.Remove(enumerator);

        foreach (var pair in toAdd)
            enumerators[pair.Key] = pair.Key.Current;

        if (enumerators.Count == 0)
            yield break;

        var min = enumerators.OrderBy(t => orderBy(t.Value)).FirstOrDefault();
        tag = min.Key;
        yield return min.Value;

        firstRun = false;
    }
}

该方法可以这样使用:

// Person lists are already sorted by age
MergeOrderedLists(orderedList, p => p.Age);

假设某个地方存在以下Person类:

    public class Person
    {
        public int Age { get; set; }
    }

应该保留重复,我们不关心它们在新序列中的顺序。你看到我可以使用任何明显的优化吗?

15 个答案:

答案 0 :(得分:13)

这是我的第四个(感谢@tanascius推动这个更多的LINQ)切入它:

public static IEnumerable<T> MergePreserveOrder3<T, TOrder>(
    this IEnumerable<IEnumerable<T>> aa,
    Func<T, TOrder> orderFunc)
where TOrder : IComparable<TOrder>
{
    var items = aa.Select(xx => xx.GetEnumerator()).Where(ee => ee.MoveNext())
        .OrderBy(ee => orderFunc(ee.Current)).ToList();

    while (items.Count > 0)
    {
        yield return items[0].Current;

        var next = items[0];
        items.RemoveAt(0);
        if (next.MoveNext())
        {
            // simple sorted linear insert
            var value = orderFunc(next.Current);
            var ii = 0;
            for ( ; ii < items.Count; ++ii)
            {
                if (value.CompareTo(orderFunc(items[ii].Current)) <= 0)
                {
                    items.Insert(ii, next);
                    break;
                }
            }

            if (ii == items.Count) items.Add(next);
        }
        else next.Dispose(); // woops! can't forget IDisposable
    }
}

结果:

for (int p = 0; p < people.Count; ++p)
{
    Console.WriteLine("List {0}:", p + 1);
    Console.WriteLine("\t{0}", String.Join(", ", people[p].Select(x => x.Name)));
}

Console.WriteLine("Merged:");
foreach (var person in people.MergePreserveOrder(pp => pp.Age))
{
    Console.WriteLine("\t{0}", person.Name);
}

List 1:
        8yo, 22yo, 47yo, 49yo
List 2:
        35yo, 47yo, 60yo
List 3:
        28yo, 55yo, 64yo
Merged:
        8yo
        22yo
        28yo
        35yo
        47yo
        47yo
        49yo
        55yo
        60yo
        64yo

改进了.Net 4.0的Tuple支持:

public static IEnumerable<T> MergePreserveOrder4<T, TOrder>(
    this IEnumerable<IEnumerable<T>> aa,
    Func<T, TOrder> orderFunc) where TOrder : IComparable<TOrder>
{
    var items = aa.Select(xx => xx.GetEnumerator())
                  .Where(ee => ee.MoveNext())
                  .Select(ee => Tuple.Create(orderFunc(ee.Current), ee))
                  .OrderBy(ee => ee.Item1).ToList();

    while (items.Count > 0)
    {
        yield return items[0].Item2.Current;

        var next = items[0];
        items.RemoveAt(0);
        if (next.Item2.MoveNext())
        {
            var value = orderFunc(next.Item2.Current);
            var ii = 0;
            for (; ii < items.Count; ++ii)
            {
                if (value.CompareTo(items[ii].Item1) <= 0)
                {   // NB: using a tuple to minimize calls to orderFunc
                    items.Insert(ii, Tuple.Create(value, next.Item2));
                    break;
                }
            }

            if (ii == items.Count) items.Add(Tuple.Create(value, next.Item2));
        }
        else next.Item2.Dispose(); // woops! can't forget IDisposable
    }
}

答案 1 :(得分:11)

有人猜测我会提高清晰度和性能是这样的:

  • 根据您在T
  • 上的比较函数订购的成对IEnumerable<T>T创建优先级排队
  • 对于要合并的每个IEnumerable<T>,将项目添加到优先级队列中,并使用对其所在的IEnumerable<T>的引用进行注释
  • 优先级队列不为空
    • 从优先级队列中提取最小元素
    • 将其注释中的IEnumerable<T>推进到下一个元素
    • 如果MoveNext()返回true,则将下一个元素添加到优先级队列中,并添加对您刚才提出的IEnumerable<T>的引用
    • 如果MoveNext()返回false,请不要向优先级队列添加任何内容
    • 产生出列元素

答案 2 :(得分:6)

这是一个具有非常好的复杂性分析的解决方案,并且比所提出的其他解决方案短得多。

public static IEnumerable<T> Merge<T>(this IEnumerable<IEnumerable<T>> self) 
    where T : IComparable<T>
{
    var es = self.Select(x => x.GetEnumerator()).Where(e => e.MoveNext());
    var tmp = es.ToDictionary(e => e.Current);
    var dict = new SortedDictionary<T, IEnumerator<T>>(tmp);
    while (dict.Count > 0)
    {
        var key = dict.Keys.First();
        var cur = dict[key];
        dict.Remove(key);
        yield return cur.Current;
        if (cur.MoveNext())
            dict.Add(cur.Current, cur);                    
    }
}

答案 3 :(得分:5)

您希望合并多少个列表?如果要合并许多不同的列表,看起来您的算法效率不高。这一行就是问题所在:

var min = enumerators.OrderBy(t => orderBy(t.Value)).FirstOrDefault();

对于所有列表中的每个元素,这将运行一次,因此您的运行时将为O(n * m),其中n是所有列表中的TOTAL元素数,n是列表数。根据列表列表中列表的平均长度表示,运行时为O(a * m ^ 2)。

如果您需要合并很多列表,我建议您使用heap。然后,每次迭代都可以从堆中删除最小值,并从列表中将最小值添加到堆中。

答案 4 :(得分:5)

这是一个没有分类的解决方案......只是最小数量的比较。 (为简单起见,我省略了实际的顺序func传递)。更新以构建平衡树: -

    /// <summary>
    /// Merge a pair of ordered lists
    /// </summary>
    public static IEnumerable<T> Merge<T>(IEnumerable<T> aList, IEnumerable<T> bList)
        where T:IComparable<T>
    {
        var a = aList.GetEnumerator();
        bool aOK = a.MoveNext();

        foreach (var b in bList)
        {
            while (aOK && a.Current.CompareTo(b) <= 0) {yield return a.Current; aOK = a.MoveNext();}
            yield return b;
        }
        // And anything left in a
        while (aOK) { yield return a.Current; aOK = a.MoveNext(); }
    }

    /// <summary>
    /// Merge lots of sorted lists
    /// </summary>
    public static IEnumerable<T> Merge<T>(IEnumerable<IEnumerable<T>> listOfLists)
        where T : IComparable<T>
    {
        int n = listOfLists.Count();
        if (n < 2) 
            return listOfLists.FirstOrDefault();
        else
            return Merge (Merge(listOfLists.Take(n/2)), Merge(listOfLists.Skip(n/2)));
    }


public static void Main(string[] args)
{

    var sample = Enumerable.Range(1, 5).Select((i) => Enumerable.Range(i, i+5).Select(j => string.Format("Test {0:00}", j)));

    Console.WriteLine("Merged:");
    foreach (var result in Merge(sample))
    {
        Console.WriteLine("\t{0}", result);
    }

答案 5 :(得分:2)

这是我的解决方案:
该算法采用每个列表的第一个元素并将它们放在一个小的辅助类(一个接受具有相同值的多个元素的排序列表)中。此排序列表使用二进制插入 所以这个列表中的第一个元素是我们想要返回的元素。执行此操作后,我们将其从排序列表中删除,并从其原始源列表中插入下一个元素(至少只要此列表包含更多元素)。同样,我们可以返回排序列表的第一个元素。当排序列表为空时,我们使用了来自所有不同源列表的所有元素并完成。

此解决方案在每个步骤中使用较少的foreach语句而不使用OrderBy - 这应该可以改善运行时行为。只有二​​进制插入必须一次又一次地完成。

IEnumerable<T> MergeOrderedLists<T, TOrder>( IEnumerable<IEnumerable<T>> orderedlists, Func<T, TOrder> orderBy )
{
    // Get an enumerator for each list, create a sortedList
    var enumerators = orderedlists.Select( enumerable => enumerable.GetEnumerator() );
    var sortedEnumerators = new SortedListAllowingDoublets<TOrder, IEnumerator<T>>();

    // Point each enumerator onto the first element
    foreach( var enumerator in enumerators )
    {
        // Missing: assert true as the return value
        enumerator.MoveNext();

        //  Initially add the first value
        sortedEnumerators.AddSorted( orderBy( enumerator.Current ), enumerator );
    }

    // Continue as long as we have elements to return
    while( sortedEnumerators.Count != 0 )
    {
        // The first element of the sortedEnumerator list always
        // holds the next element to return
        var enumerator = sortedEnumerators[0].Value;

        // Return this enumerators current value
        yield return enumerator.Current;

        // Remove the element we just returned
        sortedEnumerators.RemoveAt( 0 );

        // Check if there is another element in the list of the enumerator
        if( enumerator.MoveNext() )
        {
            // Ok, so add it to the sorted list
            sortedEnumerators.AddSorted( orderBy( enumerator.Current ), enumerator );
        }
    }

我的助手类(使用简单的二进制插入):

private class SortedListAllowingDoublets<TOrder, T> : Collection<KeyValuePair<TOrder, T>> where T : IEnumerator
{
    public void AddSorted( TOrder value, T enumerator )
    {
        Insert( GetSortedIndex( value, 0, Count - 1 ), new KeyValuePair<TOrder, T>( value, enumerator ) );
    }

    private int GetSortedIndex( TOrder item, int startIndex, int endIndex )
    {
        if( startIndex > endIndex )
        {
            return startIndex;
        }
        var midIndex = startIndex + ( endIndex - startIndex ) / 2;
        return Comparer<TOrder>.Default.Compare( this[midIndex].Key, item ) < 0 ? GetSortedIndex( item, midIndex + 1, endIndex ) : GetSortedIndex( item, startIndex, midIndex - 1 );
    }
}

现在没有实施:检查一个空列表,这会导致问题 并且可以改进SortedListAllowingDoublets类来进行比较,而不是单独使用Comparer<TOrder>.Default

答案 6 :(得分:1)

我的sixlettervariables版本的答案。我减少了对orderFunc的调用次数(每个元素只通过orderFunc一次),并且在绑定的情况下,跳过排序。这针对少量源,每个源中的大量元素以及可能昂贵的orderFunc进行了优化。

public static IEnumerable<T> MergePreserveOrder<T, TOrder>(
  this IEnumerable<IEnumerable<T>> sources, 
  Func<T, TOrder> orderFunc)  
  where TOrder : IComparable<TOrder> 
{
  Dictionary<TOrder, List<IEnumerable<T>>> keyedSources =
    sources.Select(source => source.GetEnumerator())
      .Where(e => e.MoveNext())
      .GroupBy(e => orderFunc(e.Current))
      .ToDictionary(g => g.Key, g => g.ToList()); 

  while (keyedSources.Any())
  {
     //this is the expensive line
    KeyValuePair<TOrder, List<IEnumerable<T>>> firstPair = keyedSources
      .OrderBy(kvp => kvp.Key).First();

    keyedSources.Remove(firstPair.Key);
    foreach(IEnumerable<T> e in firstPair.Value)
    {
      yield return e.Current;
      if (e.MoveNext())
      {
        TOrder newKey = orderFunc(e.Current);
        if (!keyedSources.ContainsKey(newKey))
        {
          keyedSources[newKey] = new List<IEnumerable<T>>() {e};
        }
        else
        {
          keyedSources[newKey].Add(e);
        }
      }
    }
  }
}

我打赌这可以通过SortedDictionary进一步改进,但是我没有勇气尝试使用没有编辑器的解决方案。

答案 7 :(得分:1)

以下是基于Wintellect's OrderedBag

的Linq友好解决方案
public static IEnumerable<T> MergeOrderedLists<T, TOrder>(this IEnumerable<IEnumerable<T>> orderedLists, Func<T, TOrder> orderBy)
    where TOrder : IComparable<TOrder>
{
    var enumerators = new OrderedBag<IEnumerator<T>>(orderedLists
        .Select(enumerable => enumerable.GetEnumerator())
        .Where(enumerator => enumerator.MoveNext()),
        (x, y) => orderBy(x.Current).CompareTo(orderBy(y.Current)));
    while (enumerators.Count > 0)
    {
        IEnumerator<T> minEnumerator = enumerators.RemoveFirst();
        T minValue = minEnumerator.Current;
        if (minEnumerator.MoveNext())
            enumerators.Add(minEnumerator);
        else
            minEnumerator.Dispose();
        yield return minValue;
    }
}

如果您使用任何基于枚举器的解决方案,不要忘记来调用 Dispose()

这是一个简单的测试:

[Test]
public void ShouldMergeInOrderMultipleOrderedListWithDuplicateValues()
{
    // given
    IEnumerable<IEnumerable<int>> orderedLists = new[]
    {
        new [] {1, 5, 7},
        new [] {1, 2, 4, 6, 7}
    };

    // test
    IEnumerable<int> merged = orderedLists.MergeOrderedLists(i => i);

    // expect
    merged.ShouldAllBeEquivalentTo(new [] { 1, 1, 2, 4, 5, 6, 7, 7 });
}

答案 8 :(得分:0)

这看起来像是一个非常有用的功能,所以我决定采取刺。我的方法很像heightechrider,因为它将问题分解为将两个已排序的IEnumerables合并为一个,然后将其与列表中的下一个合并。您可以进行一些优化,但它适用于我的简单测试用例:

      public static IEnumerable<T> mergeSortedEnumerables<T>(
            this IEnumerable<IEnumerable<T>> listOfLists, 
            Func<T, T, Boolean> func)
      {
            IEnumerable<T> l1 = new List<T>{};
            foreach (var l in listOfLists)
            {
                 l1 = l1.mergeTwoSorted(l, func);
            }

            foreach (var t in l1)
            {
                 yield return t;
            }
      }

      public static IEnumerable<T> mergeTwoSorted<T>(
            this IEnumerable<T> l1, 
            IEnumerable<T> l2, 
            Func<T, T, Boolean> func)
      {
            using (var enumerator1 = l1.GetEnumerator())
            using (var enumerator2 = l2.GetEnumerator())
            {
                 bool enum1 = enumerator1.MoveNext();
                 bool enum2 = enumerator2.MoveNext();
                 while (enum1 || enum2)
                 {
                      T t1 = enumerator1.Current;
                      T t2 = enumerator2.Current;

                      //if they are both false
                      if (!enum1 && !enum2)
                      {
                            break;
                      }
                      //if enum1 is false
                      else if (!enum1)
                      {
                            enum2 = enumerator2.MoveNext();
                            yield return t2;

                      }
                      //if enum2 is false
                      else if (!enum2)
                      {
                            enum1 = enumerator1.MoveNext();
                            yield return t1;

                      }
                      //they are both true
                      else
                      {
                            //if func returns true then t1 < t2
                            if (func(t1, t2))
                            {
                                 enum1 = enumerator1.MoveNext();
                                 yield return t1;

                            }
                            else
                            {
                                 enum2 = enumerator2.MoveNext();
                                 yield return t2;

                            }
                      }
                 }
            }
      }

然后测试它:

                List<int> ws = new List<int>() { 1, 8, 9, 16, 17, 21 };
                List<int> xs = new List<int>() { 2, 7, 10, 15, 18 };
                List<int> ys = new List<int>() { 3, 6, 11, 14 };
                List<int> zs = new List<int>() { 4, 5, 12, 13, 19, 20 };
                List<IEnumerable<int>> lss = new List<IEnumerable<int>> { ws, xs, ys, zs };

                foreach (var v in lss.mergeSortedEnumerables(compareInts))
                {
                     Console.WriteLine(v);
                }

答案 9 :(得分:0)

今晚我被问到这个问题是一个面试问题,并且在分配的20分钟内没有得到很好的答案。所以我强迫自己编写算法而不进行任何搜索。限制是输入已经排序。这是我的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Merger
{
  class Program
  {
    static void Main(string[] args)
    {
      int[] a = { 1, 3, 6, 102, 105, 230 };
      int[] b = { 101, 103, 112, 155, 231 };

      var mm = new MergeMania();

      foreach(var val in mm.Merge<int>(a, b))
      {
        Console.WriteLine(val);
      }
      Console.ReadLine();
    }
  }

  public class MergeMania
  {
    public IEnumerable<T> Merge<T>(params IEnumerable<T>[] sortedSources) 
      where T : IComparable
    {
      if (sortedSources == null || sortedSources.Length == 0) 
        throw new ArgumentNullException("sortedSources");

      //1. fetch enumerators for each sourc
      var enums = (from n in sortedSources 
             select n.GetEnumerator()).ToArray();

      //2. fetch enumerators that have at least one value
      var enumsWithValues = (from n in enums 
                   where n.MoveNext() 
                   select n).ToArray();
      if (enumsWithValues.Length == 0) yield break; //nothing to iterate over

      //3. sort by current value in List<IEnumerator<T>>
      var enumsByCurrent = (from n in enumsWithValues 
                  orderby n.Current 
                  select n).ToList();
      //4. loop through
      while (true)
      {
        //yield up the lowest value
        yield return enumsByCurrent[0].Current;

        //move the pointer on the enumerator with that lowest value
        if (!enumsByCurrent[0].MoveNext())
        {
          //remove the first item in the list
          enumsByCurrent.RemoveAt(0);

          //check for empty
          if (enumsByCurrent.Count == 0) break; //we're done
        }
        enumsByCurrent = enumsByCurrent.OrderBy(x => x.Current).ToList();
      }
    }
  }
}

希望它有所帮助。

答案 10 :(得分:0)

尝试改进@ cdiggins的answer。 如果比较为相等的两个元素包含在两个不同的序列中(即没有@ChadHenderson提到的缺陷),则此实现可正常工作。

该算法描述为in Wikipedia,复杂度为O( m log n ),其中 n 是正在合并的列表, m 是列表长度的总和。

使用来自Wintellect.PowerCollectionsOrderedBag<T>而不是基于堆的优先级队列,但它不会改变复杂性。

public static IEnumerable<T> Merge<T>(
   IEnumerable<IEnumerable<T>> listOfLists,
   Func<T, T, int> comparison = null)
{
   IComparer<T> cmp = comparison != null
      ? Comparer<T>.Create(new Comparison<T>(comparison))
      : Comparer<T>.Default;
   List<IEnumerator<T>> es = listOfLists
      .Select(l => l.GetEnumerator())
      .Where(e => e.MoveNext())
      .ToList();
   var bag = new OrderedBag<IEnumerator<T>>(
      (e1, e2) => cmp.Compare(e1.Current, e2.Current));
   es.ForEach(e => bag.Add(e));
   while (bag.Count > 0)
   {
      IEnumerator<T> e = bag.RemoveFirst();
      yield return e.Current;
      if (e.MoveNext())
      {
         bag.Add(e);
      }
   }
}

答案 11 :(得分:0)

要合并的每个列表都应该已经排序。该方法将根据列表的顺序定位相等的元素。例如,如果元素Ti == Tj,并且它们分别来自列表i和列表j(i

public static IEnumerable<T> Merge<T, TOrder>(this IEnumerable<IEnumerable<T>> TEnumerable_2, Func<T, TOrder> orderFunc, IComparer<TOrder> cmp=null)
{
    if (cmp == null)
    {
        cmp = Comparer<TOrder>.Default;
    }

    List<IEnumerator<T>> TEnumeratorLt = TEnumerable_2
       .Select(l => l.GetEnumerator())
       .Where(e => e.MoveNext())
       .ToList();

    while (TEnumeratorLt.Count > 0)
    {
        int intMinIndex;
        IEnumerator<T> TSmallest = TEnumeratorLt.GetMin(TElement => orderFunc(TElement.Current), out intMinIndex, cmp);
        yield return TSmallest.Current;

        if (TSmallest.MoveNext() == false)
        {
            TEnumeratorLt.RemoveAt(intMinIndex);
        }
    }
}

/// <summary>
/// Get the first min item in an IEnumerable, and return the index of it by minIndex
/// </summary>
public static T GetMin<T, TOrder>(this IEnumerable<T> self, Func<T, TOrder> orderFunc, out int minIndex, IComparer<TOrder> cmp = null)
{
    if (self == null) throw new ArgumentNullException("self");            

    IEnumerator<T> selfEnumerator = self.GetEnumerator();
    if (!selfEnumerator.MoveNext()) throw new ArgumentException("List is empty.", "self");

    if (cmp == null) cmp = Comparer<TOrder>.Default;

    T min = selfEnumerator.Current;
    minIndex = 0;
    int intCount = 1;
    while (selfEnumerator.MoveNext ())
    {
        if (cmp.Compare(orderFunc(selfEnumerator.Current), orderFunc(min)) < 0)
        {
            min = selfEnumerator.Current;
            minIndex = intCount;
        }
        intCount++;
    }

    return min;
}

答案 12 :(得分:0)

我采取了更多功能性的方法,希望这读得很好。

首先,这里是合并方法本身:

public static IEnumerable<T> MergeSorted<T>(IEnumerable<IEnumerable<T>> xss) where T :IComparable
{
    var stacks = xss.Select(xs => new EnumerableStack<T>(xs)).ToList();

    while (true)
    {
        if (stacks.All(x => x.IsEmpty)) yield break;

        yield return 
            stacks
                .Where(x => !x.IsEmpty)
                .Select(x => new { peek = x.Peek(), x })
                .MinBy(x => x.peek)
                .x.Pop();
    }
}

我们的想法是,我们将每个IEnumerable变为EnumerableStack,其中包含Peek()Pop()IsEmpty成员。

它就像常规堆栈一样工作。请注意,调用IsEmpty可能会枚举包裹的IEnumerable

以下是代码:

/// <summary>
/// Wraps IEnumerable in Stack like wrapper
/// </summary>
public class EnumerableStack<T>
{
    private enum StackState
    {
        Pending,
        HasItem,
        Empty
    }

    private readonly IEnumerator<T> _enumerator;

    private StackState _state = StackState.Pending;

    public EnumerableStack(IEnumerable<T> xs)
    {
        _enumerator = xs.GetEnumerator();
    }

    public T Pop()
    {
        var res = Peek(isEmptyMessage: "Cannot Pop from empty EnumerableStack");
        _state = StackState.Pending;
        return res;
    }

    public T Peek()
    {
        return Peek(isEmptyMessage: "Cannot Peek from empty EnumerableStack");
    }

    public bool IsEmpty
    {
        get
        {
            if (_state == StackState.Empty) return true;
            if (_state == StackState.HasItem) return false;
            ReadNext();
            return _state == StackState.Empty;
        }
    }

    private T Peek(string isEmptyMessage)
    {
        if (_state != StackState.HasItem)
        {
            if (_state == StackState.Empty) throw new InvalidOperationException(isEmptyMessage);
            ReadNext();
            if (_state == StackState.Empty) throw new InvalidOperationException(isEmptyMessage);
        }
        return _enumerator.Current;
    }

    private void ReadNext()
    {
        _state = _enumerator.MoveNext() ? StackState.HasItem : StackState.Empty;
    }
}

最后,这是MinBy扩展方法,以防无法自己编写一个:

public static T MinBy<T, TS>(this IEnumerable<T> xs, Func<T, TS> selector) where TS : IComparable
{
    var en = xs.GetEnumerator();
    if (!en.MoveNext()) throw new Exception();

    T max = en.Current;
    TS maxVal = selector(max);
    while(en.MoveNext())
    {
        var x = en.Current;
        var val = selector(x);
        if (val.CompareTo(maxVal) < 0)
        {
            max = x;
            maxVal = val;
        }
    }

    return max;
}

答案 13 :(得分:0)

这是另一种解决方案:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;
using System.Text.RegularExpressions;

namespace ConsoleApplication1
{

    class Person
    {
        public string Name
        {
            get;
            set;
        }

        public int Age
        {
            get;
            set;
        }
    }

    public class Program
    {
        public static void Main()
        {
            Person[] persons1 = new Person[] { new Person() { Name = "Ahmed", Age = 20 }, new Person() { Name = "Ali", Age = 40 } };
            Person[] persons2 = new Person[] { new Person() { Name = "Zaid", Age = 21 }, new Person() { Name = "Hussain", Age = 22 } };
            Person[] persons3 = new Person[] { new Person() { Name = "Linda", Age = 19 }, new Person() { Name = "Souad", Age = 60 } };

            Person[][] personArrays = new Person[][] { persons1, persons2, persons3 };

            foreach(Person person in MergeOrderedLists<Person, int>(personArrays, person => person.Age))
            {
                Console.WriteLine("{0} {1}", person.Name, person.Age);
            }

            Console.ReadLine();
        }

        static IEnumerable<T> MergeOrderedLists<T, TOrder>(IEnumerable<IEnumerable<T>> orderedlists, Func<T, TOrder> orderBy)
        {
            List<IEnumerator<T>> enumeratorsWithData = orderedlists.Select(enumerable => enumerable.GetEnumerator())
                                                                   .Where(enumerator => enumerator.MoveNext()).ToList();

            while (enumeratorsWithData.Count > 0)
            {
                IEnumerator<T> minEnumerator = enumeratorsWithData[0];
                for (int i = 1; i < enumeratorsWithData.Count; i++)
                    if (((IComparable<TOrder>)orderBy(minEnumerator.Current)).CompareTo(orderBy(enumeratorsWithData[i].Current)) >= 0)
                        minEnumerator = enumeratorsWithData[i];

                yield return minEnumerator.Current;

                if (!minEnumerator.MoveNext())
                    enumeratorsWithData.Remove(minEnumerator);
            }             
        }
    }   
}

答案 14 :(得分:-2)

我很怀疑LINQ足够聪明,可以利用先前现有的排序顺序:

IEnumerable<string> BiggerSortedList =  BigListOne.Union(BigListTwo).OrderBy(s => s);