Trova nodi scambiati in un BST

voti
6

Sto cercando di scrivere un programma in grado di rilevare e stampare due nodi in BST che sono stati scambiati.

In un albero a tre livelli, ho raggiunto vicino alla soluzione utilizzando questo approccio.

If (!AllSubTreeAreValid())
{
//Nodes swapped on same side of main root node
}
else
{
  int max = getMax(root->left);
  int min = getMin(root->right);
  if(max > root->data || min < root->data )
  {
     //Nodes swapped on different sides of main root node
     //Print max and min values

  }
else 
{
 //No node swappped
}
}

//Helper functions
int GetMaxEle(TreeNode* tree)
{
    while(tree->right!=NULL)
    {
        tree=tree->right;
    }
    return tree->info;
}

int GetMinEle(TreeNode* tree)
{
    while(tree->left!=NULL)
    {
        tree=tree->left;
    }
    return tree->info;
}

ma l'approccio di cui sopra non è riuscita quando ho cercato di testare con quattro alberi di livello.

             20

      15            30

   10    17       25     33

9  16  12  18  22  26  31  34

Essendo in sottoalbero destro radice nodo 15, 12 è ancora maggiore (violazione).

Essendo in sottoalbero sinistro radice nodo 15, 16 è ancora maggiore (violazione).

Così, 16, 12 sono gli elementi scambiati nel BST sopra. Come li trovo attraverso il programma?

È pubblicato 31/08/2011 alle 17:30
fonte dall'utente
In altre lingue...                            


3 risposte

voti
0

Credo che il tuo getMin et getMax funziona dormivamo con le ipotesi che l'albero è un BST, così

T getMax(tree) {
  return tree -> right == null 
    ? tree -> value 
    : getMax(tree -> right);
}

(O il codice equivalente con un anello). In tal caso, il codice prende in esame al massimo tre valori nella struttura. Anche se getMax e getMin attraversato il pieno albero per ottenere l'attuale max / min, si sarebbe ancora basare il test solo su due confronti. Se si desidera controllare che il vostro albero di soddisfare la proprietà BST, è ovvio che è necessario esaminare tutti i valori. Il suo abbastanza per confrontare ogni nodo con il suo genitore.

void CheckIsBst(Tree *tree) {
  if (tree -> left != null) {
    if (tree -> left -> value > tree -> value) {
      // print violation
    }
    CheckIsBst(tree -> left);   
  }
  // same with -> right, reversing < to > in the test
}

Edit : ciò che era sbagliato, vedi commento. Credo che questo è ok.

void checkIsBst(Tree *Tree, Tree *lowerBound, Tree *upperBound) {
  if(lowerBound!= null && lowerBound -> value > tree -> Value) {
    //violation
  }
  // same for upper bound, check with <
  if (tree -> left != null) {
    if (tree -> left -> value > tree -> value) {
      // print violation
     }
     CheckIsBst(tree -> left, lowerBound, tree);   
  }
  // same for right, reversing comparison 
  // setting lowerBound to tree instead of upperBound
}

Chiamate da radicolare con limiti nulli

Risposto il 31/08/2011 a 18:03
fonte dall'utente

voti
8

Un modo di pensare a questo problema è quello di utilizzare il fatto che un ordine simmetrico a piedi l'albero produrrà tutti gli elementi in modo ordinato. Se è possibile rilevare deviazioni dal modo ordinato durante questa passeggiata, si può provare a individuare i due elementi che sono nel posto sbagliato.

Vediamo come fare questo per un semplice array ordinato prima, quindi userà il nostro algoritmo per costruire qualcosa che funziona sugli alberi. Intuitivamente, se si parte con un array ordinato e poi scambiare due elementi (non uguali!), Noi finire con un numero di elementi nella matrice essendo fuori posto. Ad esempio, data la gamma

1 2 3 4 5

Se invertire 2 e 4, si finisce con questa matrice:

1 4 3 2 5

Come potremmo rilevare che il 2 e 4 sono stati scambiati qui? Ebbene, poiché 4 è il maggiore tra i due elementi e furono permutati verso il basso, esso dovrebbe essere maggiore di entrambi gli elementi intorno ad esso. Analogamente, poiché 2 furono permutati up, dovrebbe essere inferiore entrambi gli elementi intorno ad esso. Da questo, si potrebbe concludere che 2 e 4 sono stati scambiati.

Tuttavia, questo non sempre funziona correttamente. Ad esempio, supponiamo che ci scambiamo 1 e 4:

4 2 3 1 5

Qui, sia 2 e 1 sono più piccoli dei loro elementi adiacenti, ed entrambi 4 e 3 sono più grandi di loro. Da questo possiamo dire che due di questi quattro in qualche modo sono stati scambiati, ma non è chiaro quali dovremmo scambiare. Tuttavia, se prendiamo il più grande e il più piccolo di questi valori (rispettivamente 1 e 4,), si finisce per ottenere la coppia che è stato scambiato.

Più in generale, di trovare gli elementi che sono stati scambiati nella sequenza, si desidera trovare

  • Il più grande massimo locale nella matrice.
  • Il più piccolo minimo locale nella matrice.

Questi due elementi sono fuori luogo e devono essere scambiati.

Ora, pensiamo a come applicare questo per alberi. Dal momento che un ordine simmetrico a piedi l'albero produrrà la sequenza ordinata con i due elementi su ordine, una possibilità sarebbe quella di camminare l'albero, registrare la sequenza in ordine simmetrico degli elementi che abbiamo trovato, quindi utilizzando l'algoritmo di cui sopra. Ad esempio, si consideri il tuo BST originale:

              20
         /         \
      15             30
     /   \         /   \ 
   10    17      25     33
  / |   /  \    /  \    |  \
9  16  12  18  22  26  31  34

Se noi Linearizzare questo in un array, otteniamo

9 10 16 15 12 17 18 20 22 25 26 30 31 33 34

Si noti che è maggiore di 16 elementi circostanti e 12 è inferiore al suo. Questo ci dice subito che il 12 e 16 sono stati scambiati.

Un semplice algoritmo per risolvere questo problema, dunque, sarebbe quella di fare una passeggiata in ordine simmetrico dell'albero di linearizzare in una sequenza come un vectoro deque, quindi per eseguire la scansione quella sequenza per trovare il più grande massimo locale ed il minimo locale più piccolo. Questo sarebbe eseguito in O (n), utilizzando O (n) spazio. Un algoritmo efficiente dello spazio più difficile ma più sarebbe tenere traccia solamente di tre nodi alla volta - il nodo corrente, il suo predecessore, e il suo successore - che riduce l'utilizzo della memoria a O (1).

Spero che questo ti aiuti!

Risposto il 31/08/2011 a 21:22
fonte dall'utente

voti
0

l'attraversamento albero fatto da templatetypedef funziona se si è sicuri c'è un solo scambio. In caso contrario, vi suggerisco una soluzione basata sul codice iniziale:

int GetMax(TreeNode* tree) {
    int max_right, max_left, ret;

    ret = tree->data;
    if (tree->left != NULL) {
        max_left = GetMax(tree->left);
        if (max_left > ret)
            ret = max_left;
    }
    if (tree->right != NULL) {
        max_right = GetMax(tree->right);
        if (max_right > ret)
            ret = max_right;
    }

    return ret;
}

int GetMin(TreeNode* tree) {
    int min_right, min_left, ret;

    ret = tree->data;
    if (tree->left != NULL) {
        min_left = GetMin(tree->left);
        if (min_left < ret)
            ret = min_left;
    }
    if (tree->right != NULL) {
        min_right = GetMin(tree->right);
        if (min_right < ret)
            ret = min_right;
    }

    return ret;
}

void print_violations(TreeNode* tree) {
    if ((tree->left != NULL) && (tree->right != NULL)) {
        int max_left = GetMax(tree->left);
        int min_right = GetMin(tree->right);
        if (max_left > tree->data && min_right < tree->data) {
            printf("Need to swap %d with %d\n", max_left, min_right);
        }
    }
    if (tree->left != NULL)
        print_violations(tree->left);
    if (tree->right != NULL)
        print_violations(tree->right);
}

E 'più lento, ma stampa tutti gli swap si identifica. Può essere modificata per stampare tutte le violazioni (ad esempio, se (max_left> Tree-> Dati) violazione di stampa). È possibile migliorare le prestazioni se è possibile aggiungere due campi alla TreeNode con il max e min precalcolate per questo sotto-albero.

Risposto il 01/09/2011 a 08:43
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more