2009-06-25 4 views
6

У меня есть древовидная структура. Каждый элемент этой структуры должен иметь возможность возвращать Enumerable из всех элементов, к которым он принадлежит root. Назовем этот метод IEnumerable<Foo> GetAll(). Так что, если у нас естьПроизводительность вложенного урожая в дереве

 A <-- topmost root 
    / \ 
    B  C 
/\ /\ 
    D E F G 

исходящий вызов в GetAll на элемент C возвращает {C, F, G} (фиксированный порядок элементов было бы хорошо, но не требуется). Думаю, все это уже знали.

Текущая реализация GetAll выглядит следующим образом:

public IEnumerable<Foo> GetAll() 
{ 
    yield return this; 

    foreach (Foo foo in MyChildren) { 
     foreach (Foo f in foo.GetAll()) { 
      yield return f; 
     } 
    } 
} 

В предыдущей реализации, я вернулся в списке и добавил ребенок-FOOS использования List.AddRange().

Мой вопрос в том, что версия с использованием доходности реализована или если она должна быть улучшена (особенно с точки зрения производительности). Или это просто плохо, и я должен придерживаться List s (или ReadOnlyCollections) вместо этого?

ответ

10

Это, безусловно, не идеальное с точки зрения производительности - в итоге вы создаете много итераторов для больших деревьев, вместо одного итератора, который умеет эффективно перемещаться.

Некоторые записи в блоге относительно этого:

Стоит отметить, что F # имеет эквивалент из предлагается «yield foreach» с «yield!»

1

Нет, это выглядит нормально.

Посмотри на моих blog entry, он может быть полезны :)

-1

По моему предыдущему опыту, используя выход намного более эффективный, чем создание списка. Если вы используете .NET 3.5, эта реализация должна быть прекрасной. Но не забывайте, что

yield break; 

В конце. :-)

+0

Гм, почему вы хотели бы дать перерыв в конце концов в этом случае? –

+2

Зачем вам это нужно в конце? Я думал, что перечислитель автоматически закончил, когда метод Enumerable вышел ... – Bevan

+0

Хм, возможно, я неправильно понял что-то относительно использования урожая. Как я помню, я получил ошибку, если не закрыл метод с выходом break ;. Извините, если я сказал что-то глупое! Взгляните на этот вопрос ... – ShdNx

2

Лучшим решением может быть создание метода посещения, который рекурсивно пересекает дерево и использует его для сбора предметов.

Нечто подобное (предполагая, что бинарное дерево):

public class Node<T> 
{ 
    public void Visit(Action<T> action) 
    { 
     action(this); 
     left.Visit(action); 
     right.Visit(action); 
    } 

    public IEnumerable<Foo> GetAll() 
    { 
     var result = new List<T>(); 
     Visit(n => result.Add(n)); 
     return result; 
    } 
} 

Принимая этот подход

  • Избегает создания большого количества вложенных итераторы
  • Избегает создания каких-либо дополнительных списков, чем необходимо
  • Является относительно эффективным
  • Падает вниз, если вам нужна только часть l IST регулярно
+0

Он также занимает пространство O (n) вместо O (1) пространства - поэтому он эффективен с точки зрения вычислений, но не памяти. –

+0

Вы должны использовать foreach вместо левого и правого. Он не уточнил, что это btree. – Maghis

+0

Да, любой узел содержит 0+ детей. Но это не слишком большая разница. – mafu

19

Вы можете улучшить производительность, если раскатать рекурсию в стек, так что вы будете иметь только один итератор:

public IEnumerable<Foo> GetAll() 
{ 
    Stack<Foo> FooStack = new Stack<Foo>(); 
    FooStack.Push(this); 

    while (FooStack.Count > 0) 
    { 
     Foo Result = FooStack.Pop(); 
     yield return Result; 
     foreach (Foo NextFoo in Result.MyChildren) 
      FooStack.Push(NextFoo); 
    } 
} 
+0

Но у вас есть более 1 .... – leppie

+0

Нет, только один дал итератор и один итератор MyChildren на узел, в то время как оригинальные решения, имеющие один, дали итератор на узел и один итератор MyChildren на узел, а также рекурсию. – arbiter

+0

Спасибо, я действительно использовал этот дизайн в своем коде. Я все еще отмечаю ответ Джона как принятый, так как его ссылка содержит ту же идею, и он опубликовал ранее. Надеюсь, вы не против. o, o – mafu

Смежные вопросы