Znajdź zamienione węzły w BST

głosy
6

Próbuję napisać program, który może wykrywać i drukować dwa węzły w BST, które zostały zamienione.

W drzewie trzy poziomu dotarłem blisko rozwiązania z wykorzystaniem tego podejścia.

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;
}

ale powyższe podejście nie powiodło się, kiedy starałem się przetestować z czterech drzewa poziomu.

             20

      15            30

   10    17       25     33

9  16  12  18  22  26  31  34

Jako root węzła ° 15 w prawym poddrzewie, 12 jest jeszcze większa (naruszenie).

Jako root węzła ° 15 w lewym poddrzewie, 16 jest jeszcze większa (naruszenie).

Tak więc, 16, 12 są zamienione elementy powyższej BST. Jak znajdę je w ramach programu?

Utwórz 31/08/2011 o 17:30
źródło użytkownik
W innych językach...                            


3 odpowiedzi

głosy
0

Chyba twoja getMin et getMax działa witht hipotez, że jest to drzewo BST, tak

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

(Lub równoważny kod z pętli). Jeżeli tak, to kod sprawdza co najwyżej trzy wartości w drzewie. Nawet jeśli getMax i getMin ciągnie pełną drzewa, aby uzyskać rzeczywistą MAX / MIN, byś nadal opierać test na dwóch porównań. Jeśli chcesz sprawdzić, czy drzewo spełniają własności BST, jest oczywiste, że trzeba zbadać wszystkie wartości. Jej wystarczy, aby porównać każdy węzeł z jego rodzica.

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 : to było źle, patrz komentarz. Wierzę, że ten jest w porządku.

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
}

Dzwoniąc z korzenia NULL granicach

Odpowiedział 31/08/2011 o 18:03
źródło użytkownik

głosy
8

Jednym ze sposobów, aby myśleć o tym problemie jest wykorzystanie faktu, że w odległości Inorder drzewa będzie produkować wszystkich elementów posortowanych. Jeśli można wykryć odchylenia od posortowanych podczas tego spaceru, można spróbować zlokalizować dwa elementy, które są w złym miejscu.

Zobaczmy, jak to zrobić za pomocą prostego posortowanej tablicy, następnie użyje nasz algorytm zbudować coś, co działa na drzewach. Intuicyjnie, jeśli zaczniemy mecz w posortowanej tablicy, a następnie zamienić dwa (nie równe!) Elementy, możemy skończyć z pewnej liczby elementów w tablicy będącej na miejscu. Na przykład, biorąc pod uwagę szereg

1 2 3 4 5

Jeśli mamy zamienić 2 i 4, to w końcu z tej tablicy:

1 4 3 2 5

W jaki sposób możemy wykryć, że 2 i 4 zostały zamienione tutaj? Cóż, od 4 jest większa z dwóch elementów i zostało zamienione w dół, powinien być większy niż oba elementy wokół niego. Podobnie, ponieważ 2 został zamieniony w górę powinna być mniejsza niż oba elementy wokół niego. Z tego możemy wywnioskować, że 2 i 4 zostały zamienione.

Jednak to nie zawsze działa poprawnie. Na przykład, załóżmy, że mamy zamienić 1 i 4:

4 2 3 1 5

Tutaj zarówno 2 i 1 są mniejsze niż ich sąsiednich elementów, a oba 4 i 3 są większe niż ich. Z tego możemy powiedzieć, że dwa z tych czterech jakoś zostały zamienione, ale nie jest jasne, które z nich powinniśmy zamienić. Jednak jeśli weźmiemy największą i najmniejszą z tych wartości (1 i 4, odpowiednio), możemy skończyć się pary, które zostało zamienione.

Mówiąc bardziej ogólnie, aby znaleźć elementy, które zostały zamienione w sekwencji, chcesz znaleźć

  • Największe lokalne maksimum w tablicy.
  • Najmniejsza lokalne minimum w tablicy.

Te dwa elementy są na swoim miejscu i powinny zostać zamienione.

Teraz pomyślmy o tym, jak zastosować to do drzew. Ponieważ spacer Inorder drzewa przyniesie posortowanych sekwencji z dwóch elementów w porządku, jedna z opcji byłoby iść na drzewo, nagrywanie sekwencji Inorder elementów odkryliśmy, a następnie za pomocą powyższego algorytmu. Rozważmy na przykład oryginalną BST:

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

Gdybyśmy zlinearyzować to do tablicy, otrzymujemy

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

Należy zauważyć, że 16 jest większa niż otaczających elementów i 12 jest mniejsza niż jego minimalna. Ten od razu mówi nam, że 12 i 16 zostały zamienione.

Prosty algorytm do rozwiązania tego problemu, dlatego byłoby zrobić spacer Inorder drzewa do linearyzacji je w kolejności jak vectorOr deque, a następnie zeskanować tę sekwencję, aby znaleźć największy lokalne maksimum i najmniejszą lokalne minimum. Byłoby to w O (n), przy użyciu O (n) przestrzeni. Trudniejsze ale algorytm oszczędzającego byłoby tylko śledzić trzy węzły w czasie - bieżącego węzła poprzednika i następcę - co zmniejsza zużycie pamięci O (1).

Mam nadzieję że to pomoże!

Odpowiedział 31/08/2011 o 21:22
źródło użytkownik

głosy
0

przemierzania drzewo zrobione przez templatetypedef działa, jeśli jesteś pewien, że istnieje tylko jedna zamiana. W przeciwnym razie proponuję rozwiązanie oparte na początkowej kodu:

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);
}

Jest wolniejszy, ale drukuje wszystkie swapy identyfikuje. Może zostać zmieniony, aby wydrukować wszystkie naruszeń (np if (max_left> podobnego do drzewa> Dane) naruszeniu print). Można zwiększyć wydajność, jeśli można dodać dwa pola do TreeNode z max i min precomputed do tego poddrzewa.

Odpowiedział 01/09/2011 o 08:43
źródło użytkownik

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