2011-06-23 12 views
6

Często potrzebuję przechodzić drzewa hierarchicznych obiektów i wykonywać operacje na każdym z elementów po drodze. Czy istnieje ogólna akceptowana nazwa tego rodzaju operacji w języku zrozumiałym dla listy? Pytam, ponieważ pamiętam, że najpierw dowiedziałem się o Pythonie zip function, zanim miał on odpowiednik w strukturze .net i myślał, że ma nietypową, ale odpowiednią nazwę.Czy istnieje akceptowana nazwa dla tego rodzaju operacji przeliczalnych?

Oto kilka ogólnych metod rekurencyjnych w górę i w dół struktur drzewa i przynosi każdy z nich w miarę ich napotykania.

public static IEnumerable<T> Ancestors<T>(T source, Func<T, T> selector) 
{ 
    do 
    { 
     yield return source; 
     source = selector(source); 
    } while (!Equals(source, default(T))); 
} 

public static IEnumerable<T> Descendents<T>(T source, 
              Func<T, IEnumerable<T>> selector) 
{ 
    var stack = new Stack<T>(); 
    stack.Push(source); 
    while (stack.Count > 0) 
    { 
     source = stack.Pop(); 
     yield return source; 
     var items = selector(source); 
     if (items != null) 
     { 
      foreach (var item in items) 
      { 
       stack.Push(item); 
      } 
     } 
    } 
} 
+0

Jakiś rodzaj przefiltrowanego drzewa ruchu? Nie wiem, czy to ma konkretną nazwę. Nie sądzę, że tak. –

+0

Druga wykonuje wyszukiwanie głębi-pierwszego. Nie jestem pewien, czy druga nazwa ma nazwę, ponieważ chociaż jest nazywana "przodkami" w zależności od funkcji selektora, nie musi w ogóle podążać za "rodzicem" (np. Może zrobić wszystko, np. Wybrać "najlepsze" dziecko node) –

+0

@George: Dokładnie, 'Przodkowie' oznacza pewien rodzaj hierarchicznej relacji. W rzeczywistości można go z łatwością wykorzystać do przechodzenia w podwójnie połączoną listę w dowolnym kierunku lub dowolną dowolną ścieżkę. –

Odpowiedz

1

Są to standardowe Tree Traversal funkcje, powszechnie znany jako „drzewa chodzenia”. Trudno podać znormalizowane nazwy przykładów, ponieważ konkretna strategia chodzenia nie jest znana :)

7

Zakładając, że selektor daje węzły potomne, twoja druga metoda to przemiana "pierwsza pierwsza głębia pierwsza". Oznacza to, że jeśli miał

 A 
    /\ 
    B  C 
/\ /\ 
D E F G 

następnie dostać A, C, G, F, B, E, D. dostać "G" przed "B", ponieważ "głębokość najpierw" idzie tak głęboko, jak to możliwe zanim spróbuje innej gałęzi. W twoim konkretnym przykładzie dostaniesz C przed B, ponieważ pierwszeństwo ma nad lewicą.

Jeśli zmieniono go

foreach (var item in items.Reverse()) 

następnie chcesz uzyskać lewej pierwszej głębokości pierwszego przechodzenie, który jest jak większość ludzi myśli o głębokości pierwszego przechodzenia.

Po zmianie stosu na kolejkę stanie się to przejściem "od szerokości". A, B, C, D, E, F, G. Robisz cały "poziom" na raz.

Istnieją również inne ruchy. Zauważ, że pierwsze i pierwsze przeszukiwanie mają tę właściwość, że węzły nadrzędne występują przed węzłami potomnymi. Możesz także wykonywać "post-order", w których każdy węzeł przychodzi po swoich dzieciach.

Drzewa binarne również przechodzą "na zamówienie". Przemierzanie tego drzewa to D, B, E, A, F, C, G. Oznacza to, że każde lewe dziecko pojawia się przed wszystkimi jego przodkami, a każde prawe dziecko przychodzi po wszystkich swoich przodkach. Czy jako ćwiczenie możesz napisać sekwencję w kolejności na drzewie binarnym?

+1

Odpowiedź ćwiczenia PseudoCode: 'Funkcja Inorder (drzewo) {if (drzewo == null) {return;} Inorder (Tree.Left); Wyjście (Tree.Value); Inorder (Tree.Right);} ' – Brian

+0

@Brian: Super. Czy możesz to zrobić bez rekursji? –

+0

@Eric, musisz emulować stos wywołań za pomocą normalnego stosu węzłów i adresu powrotu przy użyciu flagi dla każdego węzła na stosie. Po kliknięciu stosu zaznacz flagę, czy 1. "recurse" w lewo, czy 2. (yield) zwraca aktualny węzeł i "recurse" w prawo. Nie ma potrzeby tworzenia trzeciego stanu, który jest w zasadzie optymalizacją wywołania końcowego. – svick

Powiązane problemy