Как принять двоичное дерево в качестве входных данных, когда пользователь дает края в качестве входных данных? - PullRequest
0 голосов
/ 10 апреля 2020

Предположим, я хочу взять двоичное дерево в качестве входных данных, когда пользователь дает только ребра между двумя узлами в качестве входных данных, и как мне определить, какой из них является root? Так как пользователь может задать ребра root на последней стадии ввода. Чтобы быть более точным, предположим, что ввод будет -

2 4
2 5
3 7
3 6
1 2
1 3

Здесь root - это тот, который мы узнаем после рисуя двоичное дерево, которое выглядит следующим образом: -

       1
      /  \
     2    3
    / \  / \
   4  5  6  7

Как мне построить такое двоичное дерево. Если вы думаете, что использование массива (представление списка смежности) является ответом, то извините, я хочу перейти к ICA двух узлов этого дерева, что я не могу сделать, используя вектор, который я предполагаю (было бы здорово, если бы кто-то придумал решение получить LCA двух узлов, используя представление смежных узлов). Но я думаю, что это не так. возможно в это время. Поэтому я прошу дать мне способ создать такое бинарное дерево, просто взяв ребра в качестве входных данных без предварительного ознакомления с root дерева. Спасибо, что посвятили свое драгоценное время.

Ответы [ 2 ]

0 голосов
/ 10 апреля 2020

Два решения:

1. Массив / Вектор (или карта)

Каждый узел имеет до двух дочерних элементов. Их запись позволяет спускаться по дереву.

Но - у каждого узла есть только один родительский элемент . Запись этой информации облегчает восхождение дерева - что отлично подходит для поиска root дерева или LCA для узлов. После анализа вашего ввода ваш массив выглядит следующим образом:

  ┌─┐
0 │0│
1 │0│
2 │1│
3 │1│
4 │2│
5 │2│
6 │3│
7 │3│
  └─┘

Теперь, начиная с любого узла - скажем, 6 - мы видим из массива, что родительский элемент 6 равен 3, а родительский 3 равен 1, а 1 имеет нет родителей. Если ваши ключи узла не являются списком маленьких целочисленных значений, как в вашем примере, вы должны использовать карту вместо этого.

2. Создайте двоичное дерево!

Проблема, с которой вы, вероятно, сталкиваетесь, заключается в том, что мы традиционно работаем с деревом, удерживая ссылку на root и получая доступ ко всем остальным узлам из root - но пока вы строите дерево из примера ввода, вы не только не знаете root, вы также имеете фрагменты дерева, которые объединяются в одно дерево только тогда, когда у вас есть достаточно информации.

Все, что вам нужно, это временная структура для хранения узлов (и тем самым фрагментов), пока дерево не будет завершено и не будет идентифицировано root. Затем вы можете выбросить эту структуру.

Вот некоторый непроверенный код, чтобы показать, что я имею в виду. Осторожно - он использует необработанные указатели, он использует new, у него нет deletes, древовидная структура должна быть классом, et c et c. Это просто для иллюстрации алгоритма и не рекомендуется практика кодирования.

#include <map>
#include <utility>
#include <vector>

struct node {
    node *parent, *left, *right;
    int value;
};

struct tree {
    node *root;
};

tree BuildTree(const std::vector<std::pair<int, int>>& edges) {
    std::map<int, node*> nodes;

    auto FindOrAddNode = [&nodes](int key) -> node* {
        auto node_iter = nodes.find(key);
        if (node_iter != nodes.end())
            return node_iter->second;
        else {
            auto new_node = new node;
            new_node->value = key;
            nodes[key] = new_node;
            return new_node;
        }
    };

    for (auto edge : edges) {
        auto parent = FindOrAddNode(edge.first);
        auto child = FindOrAddNode(edge.second);

        child->parent = parent;
        if (parent->left == nullptr)
            parent->left = child;
        else if (child->value >= parent->left->value)
            parent->right = child;
        else {
            parent->right = parent->left;
            parent->left = child;
        }
    }

    // now pick any node, and walk up to find the root
    // then construct and return a tree object using the root
    // when we exit this function, the local `nodes` variable is discarded
    //    we don't need it any more.
}

int main(int argc, char** argv) {
    auto the_tree = BuildTree({{2, 4}, {2, 5}, {3, 7}, {3, 6}, {1, 2}, {1, 3}});
    return 0;
}
0 голосов
/ 10 апреля 2020

"Я хочу взять двоичное дерево в качестве входных данных, когда пользователь задает только ребра между двумя узлами в качестве входных данных и как определить, какой из них является root?"

Есть ли у нас гарантия, что в вашем двоичном дереве каждый внутренний узел имеет двух дочерних элементов (в отличие от возможности иметь только один дочерний элемент)?

Если нет - тогда существует несколько кандидатов на root. В частности, любой лист можно сделать root.

Итак, допустим, что у внутренних узлов должно быть два потомка. Давайте рассмотрим количество вхождений каждого узла в списке ребер.

  • Листовые узлы появятся в списке только один раз, потому что только один ребро достигает их сверху.
  • Внутренние узлы, которые не являются root узлами, появляются в списке 3 раза. Это потому, что есть 2 ребра, идущих вниз от узла к его дочерним элементам, и одно ребро, достигающее узла сверху.
  • root - единственный узел, не имеющий родителя и имеющий двух дочерних элементов - таким образом, появляется дважды .

В вашем примере: 2 4 2 5 3 7 3 6 1 2 1 3

  • 1 - появляется дважды
  • 2, 3 - появляется 3 раза
  • 4, 5, 6, 7 - появляются 1 раз

Таким образом, 1 ваш root.

", пожалуйста, дайте мне способ создания такого двоичного дерева "

Теперь, на этот трудно ответить, если вы не объясните, каким должно быть ваше окончательное представление дерева. Список ребер является допустимым представлением, поэтому вам не нужно ничего делать. Но, очевидно, вы имеете в виду кое-что еще.

Надеюсь, поскольку я показал вам, что с помощью тривиального алгоритма подсчета вы можете найти свой root, фактически не создавая какую-либо структуру, построив свое дерево так, как вы хотите, будет проще.

...