Найти поменялись узлы в BST

голоса
6

Я пытаюсь написать программу, которая может обнаружить и напечатать два узла в BST, которые были поменяны местами.

В дереве три уровня, я достиг близко к решению, используя этот подход.

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

но выше подход потерпел неудачу, когда я пытался проверить с четырьмя дерева уровня.

             20

      15            30

   10    17       25     33

9  16  12  18  22  26  31  34

Находясь в правом поддереве корневого узла 15, в 12 еще больше (нарушение).

Находясь в левом поддереве корневого узла 15, в 16 еще больше (нарушение).

Таким образом, 16, 12 представляет собой обмениваемые элементы в приведенном выше BST. Как найти их с помощью программы?

Задан 31/08/2011 в 17:30
источник пользователем
На других языках...                            


3 ответов

голоса
0

Я предполагаю, что ваш getMin и др getMax работы witht гипотез о том, что дерево является BST, так

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

(Или эквивалентный код с петлей). Если да, то ваш код рассматривается не более трех значений в дереве. Даже если getMax и getMin пересекали полное дерево, чтобы получить фактическое макс / мин, вы по-прежнему основывают свой тест на только два сравнения. Если вы хотите, чтобы убедиться, что ваше дерево удовлетворяет свойство BST, то очевидно, что вы должны проверить все значения. Его достаточно, чтобы сравнить каждый узел с его родителем.

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 : это было не так, см комментарий. Я считаю , что это один нормально.

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
}

Вызов из корня с нулевыми границами

Ответил 31/08/2011 в 18:03
источник пользователем

голоса
8

Один из способов думать об этой проблеме заключается в использовании того факта, что Симметричная прогулка дерева будет производить все элементы в отсортированном порядке. Если вы можете обнаружить отклонение от упорядоченных во время этой прогулки, вы можете попытаться найти два элемента, которые находятся в неправильном месте.

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

1 2 3 4 5

Если поменять местами 2 и 4, мы в конечном итоге с этим массивом:

1 4 3 2 5

Как бы мы обнаружим, что 2 и 4 были заменены здесь? Ну, так как 4 является большей из двух элементов и был обменен вниз, оно должно быть больше, чем оба элемента вокруг него. Аналогичным образом, из-за 2 был обменен вверх, она должна быть меньше, чем оба из элементов вокруг него. Исходя из этого, можно сделать вывод, что 2 и 4 поменялись местами.

Однако, это не всегда работает правильно. Например, предположим, что мы переставляем 1 и 4:

4 2 3 1 5

Здесь, как и 1 2 меньше, чем их соседних элементов, и оба 4 и 3 больше, чем у них. Из этого мы можем сказать, что два из этих четырех как-то поменялись местами, но это не ясно, какие из них мы должны поменять местами. Однако, если взять самый большой и самый маленький из этих значений (1 и 4, соответственно), мы в конечном итоге получить пару, которая была выгружена.

В более общем плане, чтобы найти элементы, которые поменялись местами в последовательности, вы хотите найти

  • Наибольший локальный максимум в массиве.
  • Наименьший локальный минимум в массиве.

Эти два элемента из места и должны быть заменены.

Теперь давайте подумаем о том, как применить это к деревьям. Так как заказовМой прогулка дерева будет производить отсортированную последовательность с двумя элементами из заказа, один вариант был бы ходить по дереву, записи последовательности заказовМои элементов мы нашли, то, используя вышеупомянутый алгоритм. Например, рассмотрим исходный BST:

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

Если линеаризуем это в массив, мы получаем

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

Обратите внимание на то, что 16 больше, чем окружающие ее элементы и что 12 меньше его. Это сразу говорит нам о том, что 12 и 16 были заменены.

Простой алгоритм для решения этой проблемы, поэтому, должен был бы сделать заказовМои прогулку дерева линеаризацию его в последовательность , как vectorО.Р. deque, затем отсканировать эту последовательность , чтобы найти самый большой локальный максимум и наименьший локальный минимум. Это будет работать в O (N) времени, используя O (N) пространства. Сложнее , но более пространственно-эффективный алгоритм будет держать только след трех узлов одновременно - текущий узел, его предшественник, и его преемник - что уменьшает использование памяти в O (1).

Надеюсь это поможет!

Ответил 31/08/2011 в 21:22
источник пользователем

голоса
0

Обход дерева сделано templatetypedef работает , если вы уверены , что есть только одна замена. В противном случае я предлагаю решение , основанное на вашем исходном коде:

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

Это медленнее, но он печатает все свопы он идентифицирует. Может быть изменен для печати всех нарушений (например, если (max_left> tree-> данные) нарушение печати). Вы можете улучшить производительность, если вы можете добавить два поля в TreeNode с макс и мин предвычисленной для этого поддерева.

Ответил 01/09/2011 в 08:43
источник пользователем

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