Найдите максимальную высоту дерева

У меня есть древовидная структура данных с N дочерними узлами первого уровня, у которых тоже есть дочерние элементы.

Например:

  • Корень
    • Узел 1
      • узел 11
        • Узел 111
          • Узел 1111
      • узел 12
    • Узел2
      • узел 21
        • Узел 211

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

Node1 - Node11 - Node111 - Node1111

имеет глубину четырех уровней.

Любое предложение?

Спасибо!


person Vincenzo    schedule 21.02.2010    source источник
comment
@Moron: Что ты имеешь в виду под домашним заданием?   -  person Vincenzo    schedule 22.02.2010
comment
Ты ведь знаешь, что такое домашнее задание, да?   -  person    schedule 22.02.2010


Ответы (4)


Вы должны проверить все узлы. Несколько месяцев назад я реализовал этот алгоритм таким образом:

class Node
{
    public String Name { get; private set; }
    public IList<Node> Childs { get; private set; }

    public Node(String name)
    {
        Name = name;
        Childs = new List<Node>();
    }

    public List<Node> Depth
    {
        get
        {
            List<Node> path = new List<Node>();
            foreach (Node node in Childs)
            {
                List<Node> tmp = node.Depth;
                if (tmp.Count > path.Count)
                    path = tmp;
            }
            path.Insert(0, this);
            return path;
        }
    }

    public override string ToString()
    {
        return Name;
    }
}

Пример теста:

Node node1111 = new Node("Node1111");
Node node111 = new Node("Node111");
Node node11 = new Node("Node11");
Node node12 = new Node("Node12");
Node node1 = new Node("Node1");
Node root = new Node("Root");
Node node2 = new Node("Node2");
Node node21 = new Node("Node21");
Node node211 = new Node("Node211");
root.Childs.Add(node1);
root.Childs.Add(node2);
node1.Childs.Add(node11);
node1.Childs.Add(node12);
node11.Childs.Add(node111);
node111.Childs.Add(node1111);
node2.Childs.Add(node21);
node21.Childs.Add(node211);

List<Node> path = root.Depth;
foreach (Node n in path)
    Console.Write(String.Format("{0} - ", n.ToString()));

Console.WriteLine();

Node node2111 = new Node("Node2111");
node2111.Childs.Add(new Node("Node21111"));
node211.Childs.Add(node2111);

path = root.Depth;
foreach (Node n in path)
    Console.Write(String.Format("{0} - ", n.ToString()));

Console.WriteLine();

Вывод консоли:

Root - Node1 - Node11 - Node111 - Node1111 -
Root - Node2 - Node21 - Node211 - Node2111 - Node21111 -
person Michał Ziober    schedule 21.02.2010
comment
Это почти так же, как я это сделал, но мне пришлось реализовать это на C. - person WarmWaffles; 21.02.2010
comment
Хорошее решение. Два предложения. Во-первых, название Depth неудачное. Он не возвращает глубину. Он должен называться PathToDeepestLeaf. Во-вторых, свойства должны быть зарезервированы для (1) вещей, которые логически являются свойствами, такими как цвет или глубина, а не сложных вычисляемых вещей, таких как путь к нижнему листу, (2) вещей, которые очень быстро вычисляются, на того же порядка скорости, что и доступ к полю. Я бы сделал это методом, а не свойством. - person Eric Lippert; 21.02.2010
comment
Задача для вас: дерево может быть очень, очень глубоким. Предположим, что он имеет глубину в тысячу узлов, и такая большая рекурсия взорвет стек. Можете ли вы сделать это без рекурсии? - person Eric Lippert; 21.02.2010
comment
Спасибо за ваши советы. Попробую написать алгоритм без рекурсии. Рекурсивный вариант для меня самый простой. - person Michał Ziober; 21.02.2010
comment
Спасибо за ваши ответы и комментарии! Помимо комментариев Эрика Липперта, с которыми я почти полностью согласен, предложенное вами решение было моей первой идеей. Я закодировал с помощью рекурсии, и это сработало отлично. Но .... это немного неоптимизировано! Любое предложение по этому поводу? - person Vincenzo; 22.02.2010

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

person wallyk    schedule 21.02.2010

Обходим дерево в глубину или в ширину, назначая каждому узлу его глубину. Запомните узел с наибольшей глубиной.

Перейдите recusrivle обратно от этого узла к корню. Это дает вам самую длинную ветвь. Может быть несколько ветвей одинаковой длины.

person Carsten    schedule 21.02.2010

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

Вот глупая строчка, показывающая концепцию поиска максимально достижимой глубины файловой системы с использованием UNIX find, awk и tr:

find / -depth | tr -dc '/\n' \
    | awk '{if (length($0) > max) { max=length($0)}}; END {print max}'

... find — это итератор, tr — это манипуляция с данными, «переводящая» один набор символов в другой (в данном случае он используется для -d (удаления) дополнения (-c) указанного набора символов (/ ). Таким образом, он преобразует любой полный путь UNIX только в разделители /. Оттуда я просто нахожу самую длинную строку ввода ... и это мой результат.

Конечно, такой подход не сильно поможет вам с домашним заданием. Но концепция должна быть ясной. :)

person Jim Dennis    schedule 21.02.2010