Вот мое решение:
Алгоритм берет первый элемент каждого списка и помещает их в небольшой вспомогательный класс (отсортированный список, который принимает элементы mutliple с одинаковым значением). В этом отсортированном списке используется двоичная вставка .
Таким образом, первый элемент в этом списке - это элемент, который мы хотим вернуть следующим. После этого мы удаляем его из отсортированного списка и вставляем следующий элемент из исходного списка источников (по крайней мере, пока этот список содержит больше элементов). Опять же, мы можем вернуть первый элемент нашего отсортированного списка. Когда отсортированный список пуст один раз, мы использовали все элементы из всех разных исходных списков и сделали.
Это решение использует меньше операторов 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
самостоятельно.