Quando utilizzare le strategie di attraversamento dell'albero di ricerca binaria Preordine, Postorder e Inorder


97

Mi sono reso conto di recente che, pur avendo utilizzato in abbondanza la BST nella mia vita, non ho mai nemmeno contemplato di utilizzare altro che Inorder traversal (mentre sono consapevole e so quanto sia facile adattare un programma per utilizzare l'attraversamento pre / post-ordine).

Dopo essermi reso conto di ciò, ho tirato fuori alcuni dei miei vecchi libri di testo sulle strutture dati e ho cercato il ragionamento dietro l'utilità delle traversate pre-ordine e post-ordine - ma non hanno detto molto.

Quali sono alcuni esempi di quando utilizzare praticamente il preorder / postorder? Quando ha più senso che in ordine?

Risposte:


135

Quando utilizzare Pre-Order, In-Order e Post-Order Traversal Strategy

Prima di poter capire in quali circostanze utilizzare il pre-ordine, l'ordine in ordine e il post-ordine per un albero binario, devi capire esattamente come funziona ciascuna strategia di attraversamento. Usa il seguente albero come esempio.

La radice dell'albero è 7 , il nodo più a sinistra è 0 , il nodo più a destra è 10 .

inserisci qui la descrizione dell'immagine

Attraversamento pre-ordine :

Riepilogo: inizia dalla radice ( 7 ), termina al nodo più a destra ( 10 )

Sequenza trasversale: 7, 1, 0, 3, 2, 5, 4, 6, 9, 8, 10

Attraversamento in ordine :

Riepilogo: inizia dal nodo più a sinistra ( 0 ), termina nel nodo più a destra ( 10 )

Sequenza trasversale: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Attraversamento post-ordine :

Riepilogo: inizia con il nodo più a sinistra ( 0 ), termina con la radice ( 7 )

Sequenza trasversale: 0, 2, 4, 6, 5, 3, 1, 8, 10, 9, 7

Quando utilizzare Pre-Order, In-order o Post-Order?

La strategia di attraversamento selezionata dal programmatore dipende dalle esigenze specifiche dell'algoritmo progettato. L'obiettivo è la velocità, quindi scegli la strategia che ti porta i nodi di cui hai bisogno più velocemente.

  1. Se sai che devi esplorare le radici prima di ispezionare le foglie, scegli il preordine perché incontrerai tutte le radici prima di tutte le foglie.

  2. Se sai che devi esplorare tutte le foglie prima di qualsiasi nodo, seleziona il post-ordine perché non perdi tempo a ispezionare le radici alla ricerca di foglie.

  3. Se sai che l'albero ha una sequenza intrinseca nei nodi e vuoi appiattire l'albero nella sua sequenza originale, allora dovresti usare un attraversamento in ordine . L'albero verrebbe appiattito nello stesso modo in cui è stato creato. Un attraversamento di pre-ordine o post-ordine potrebbe non svolgere nuovamente l'albero nella sequenza utilizzata per crearlo.

Algoritmi ricorsivi per pre-ordine, in-order e post-order (C ++):

struct Node{
    int data;
    Node *left, *right;
};
void preOrderPrint(Node *root)
{
  print(root->name);                                  //record root
  if (root->left != NULL) preOrderPrint(root->left);  //traverse left if exists
  if (root->right != NULL) preOrderPrint(root->right);//traverse right if exists
}

void inOrderPrint(Node *root)
{
  if (root.left != NULL) inOrderPrint(root->left);   //traverse left if exists
  print(root->name);                                 //record root
  if (root.right != NULL) inOrderPrint(root->right); //traverse right if exists
}

void postOrderPrint(Node *root)
{
  if (root->left != NULL) postOrderPrint(root->left);  //traverse left if exists
  if (root->right != NULL) postOrderPrint(root->right);//traverse right if exists
  print(root->name);                                   //record root
}

3
Che dire degli attraversamenti non ricorsivi? Mi sembra che sia molto più facile attraversare un albero in modo non ricorsivo in pre-ordine rispetto a in-order / post-order, poiché non richiede di tornare ai nodi precedenti.
bluenote10

@ bluenote10 Puoi approfondire cosa intendi? In preordine, "ritorni" ancora a un nodo per elaborare il suo figlio destro dopo aver elaborato il suo figlio sinistro. Certo, potresti usare una coda di "nodi non ancora visitati", ma in realtà si tratta solo di scambiare memoria implicita (stack) per una coda esplicita. In tutti i metodi di attraversamento, devono essere elaborati entrambi i figli sinistro e destro, il che significa che dopo aver eseguito uno di essi devi "tornare" al genitore.
Joshua Taylor

@ JoshuaTaylor: Sì, sono tutti la stessa classe di complessità, ma se guardi alle implementazioni tipiche, il post-ordine è probabilmente un po 'più complicato.
bluenote10

2
La traversata di preordine fornisce i valori del nodo in una sequenza di inserimento. Se vuoi creare una copia dell'albero devi attraversare l'albero sorgente in questo modo. La poligonale in ordine fornisce i valori dei nodi ordinati. Per quanto riguarda la traversata post-ordine, è possibile utilizzare questo metodo per eliminare l'intero albero perché visita prima i nodi foglia.
albin

Penso che sia vero anche se l'albero non è ordinato correttamente, intendo dire che in order non darebbe la sequenza ordinata se la sequenza non fosse stata ordinata all'inizio.
CodeYogi

29

Pre-ordine: utilizzato per creare una copia di un albero. Ad esempio, se desideri creare una replica di un albero, inserisci i nodi in un array con un attraversamento di preordine. Quindi eseguire un'operazione di inserimento su un nuovo albero per ogni valore nella matrice. Ti ritroverai con una copia del tuo albero originale.

In ordine: utilizzato per ottenere i valori dei nodi in ordine non decrescente in un BST.

Post-ordine: utilizzato per eliminare un albero dalla foglia alla radice


2
questa è un'ottima risposta concisa e mi ha aiutato a capire i casi d'uso di pre-ordine e post-ordine. anche se può essere ovvio dato che la domanda lo menziona direttamente, ma si noti che questo è il caso degli alberi di RICERCA binari e non funziona necessariamente per gli alberi binari generali. ad esempio, non è possibile utilizzare necessariamente l'attraversamento del preordine per copiare un albero binario generale, poiché la logica di inserimento durante il processo di copia non funzionerebbe.
markckim

7
In ordine:: per ottenere i valori del nodo in ordine "non decrescente" - non "non crescente"
rahil008

26

Se volessi semplicemente stampare il formato gerarchico dell'albero in un formato lineare, probabilmente userei il preorder traversal. Per esempio:

- ROOT
    - A
         - B
         - C
    - D
         - E
         - F
             - G

4
O in un TreeViewcomponente in un'applicazione GUI.
svick

4

Il pre e il post-ordine si riferiscono rispettivamente ad algoritmi ricorsivi top-down e bottom-up. Se vuoi scrivere un dato algoritmo ricorsivo su alberi binari in modo iterativo, questo è ciò che essenzialmente farai.

Si osservi inoltre che le sequenze di pre e post ordine insieme specificano completamente l'albero in questione, producendo una codifica compatta (per gli alberi sparsi almeno).


1
Penso che tu stia cercando di dire qualcosa di importante, puoi spiegare la prima metà?
CodeYogi

@CodeYogi Di cosa hai bisogno nello specifico?
Raphael

1
"Il pre e il post-ordine riguardano algoritmi ricorsivi top-down e bottom-up" Penso che tu voglia dire che nel primo caso il nodo viene elaborato prima di chiamare uno qualsiasi dei metodi ricorsivi e viceversa nel secondo, giusto ?
CodeYogi

@CodeYogi Sì, fondamentalmente.
Raphael

2

Ci sono tantissimi posti in cui vedi questa differenza giocare un ruolo reale.

Uno dei migliori che sottolineerò è nella generazione di codice per un compilatore. Considera l'affermazione:

x := y + 32

Il modo in cui genereresti il ​​codice per questo è (ingenuamente, ovviamente) generare prima il codice per caricare y in un registro, caricare 32 in un registro e quindi generare un'istruzione per aggiungere i due. Perché qualcosa deve essere in un registro prima di manipolarlo (supponiamo, puoi sempre fare operandi costanti ma qualunque cosa) devi farlo in questo modo.

In generale, le risposte che puoi ottenere a questa domanda si riducono sostanzialmente a questo: la differenza è davvero importante quando c'è una certa dipendenza tra l'elaborazione di parti diverse della struttura dei dati. Si vede quando si stampano gli elementi, durante la generazione del codice (lo stato esterno fa la differenza, è possibile visualizzarlo anche monadicamente, ovviamente) o quando si eseguono altri tipi di calcoli sulla struttura che implicano calcoli a seconda dei bambini elaborati per primi .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.