2012-03-21 73 views
8

VớiCó cách nào dễ dàng để hợp nhất hai chuỗi được sắp xếp bằng LINQ không?

IEnumerable<T> first; 
IEnumerable<T> second; 

và rằng cả hai firstsecond được sắp xếp theo một Comparer Func<T, T, int> trả về 0 cho bình đẳng, -1 khi là người đầu tiên là "nhỏ" và 1 khi thứ hai là "nhỏ hơn".

Có cách nào thẳng về phía trước sử dụng LINQ để hợp nhất hai trình tự theo cách làm cho chuỗi kết quả cũng được sắp xếp bởi cùng một bộ so sánh không?

Hiện tại, chúng tôi đang sử dụng thuật toán thủ công, nhưng khả năng đọc của câu lệnh LINQ thẳng về phía trước sẽ thích hợp hơn.

+2

thể trùng lặp của [thuật toán hiệu quả nhất cho việc sáp nhập được sắp xếp IEnumerable ] (http://stackoverflow.com/questions/ 2767007/hiệu quả nhất-thuật toán-cho-hợp nhất-sắp xếp-ienumerablet) – Jon

+0

Bạn đã trích xuất thuật toán thủ công của mình thành một phương pháp riêng biệt? Đó sẽ là cách đơn giản nhất để cải thiện khả năng đọc. – phoog

+2

Nếu bạn thích khả năng đọc (trên hiệu suất) thì: 'var both = first.Union (thứ hai) .OrderBy (so sánh);' –

Trả lời

11

Bạn có thể xác định phương thức tiện ích mở rộng cho việc này. Một cái gì đó như

public static IEnumerable<T> MergeSorted<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, int> comparer) 
{ 
    using (var firstEnumerator = first.GetEnumerator()) 
    using (var secondEnumerator = second.GetEnumerator()) 
    { 

     var elementsLeftInFirst = firstEnumerator.MoveNext(); 
     var elementsLeftInSecond = secondEnumerator.MoveNext(); 
     while (elementsLeftInFirst || elementsLeftInSecond) 
     { 
      if (!elementsLeftInFirst) 
      { 
        do 
        { 
         yield return secondEnumerator.Current; 
        } while (secondEnumerator.MoveNext()); 
        yield break; 
      } 

      if (!elementsLeftInSecond) 
      { 
        do 
        { 
         yield return firstEnumerator.Current; 
        } while (firstEnumerator.MoveNext()); 
        yield break; 
      } 

      if (comparer(firstEnumerator.Current, secondEnumerator.Current) < 0) 
      { 
       yield return firstEnumerator.Current; 
       elementsLeftInFirst = firstEnumerator.MoveNext(); 
      } 
      else 
      { 
       yield return secondEnumerator.Current; 
       elementsLeftInSecond = secondEnumerator.MoveNext(); 
      } 
     } 
    } 
} 

Cách sử dụng:

var s1 = new[] { 1, 3, 5, 7, 9 }; 
var s2 = new[] { 2, 4, 6, 6, 6, 8 }; 

var merged = s1.MergeSorted(s2, (a, b) => a > b ? 1 : -1).ToList(); 

Console.WriteLine(string.Join(", ", merged)); 

Output:

1, 2, 3, 4, 5, 6, 6, 6, 7, 8, 9 
+2

Một đoạn mã đơn cực nhanh - cảm ơn! –

+0

Tham số 'comparer' có thể được cho phép là null (tham số tùy chọn) và được đặt thành' Comparer .Default.Compare' nếu nó là null. – springy76

0

Tôi nghĩ rằng, chuyển đổi đầu tiên enumerable vào danh sách và thêm mục thứ hai vào danh sách này sau đó gọi sắp xếp sẽ làm các trick.

 IEnumerable<int> first = new List<int>(){1,3}; 
     IEnumerable<int> second = new List<int>(){2,4}; 

     var temp = first.ToList(); 
     temp.AddRange(second); 


     temp.Sort(new Comparison<int>(comparer)); // where comparer is Func<T,T,int> 
+7

Thực hiện này là O (n) trong bộ nhớ thừa, O (n lg n) trong trường hợp điển hình và O (n^2) trong trường hợp xấu nhất trong thời gian. Có một thuật toán mà cả hai O (1) trong lưu trữ thêm và O (n) trong thời gian. –

Các vấn đề liên quan