Самый длинный путь между 2 узлами - PullRequest
12 голосов
/ 26 июня 2010

Рассчитайте самый длинный путь между двумя узлами.Путь в арке.Подпись метода:

public static int longestPath(Node n)

В приведенном ниже примере двоичного дерева это 4 (через 2-3-13-5-2).

Это то, что у меня есть сейчас, и для данного дерева оно просто возвращает 0.

public static int longestPath(Node n) {
    if (n != null) {
        longestPath(n, 0);
    }
    return 0;
}
private static int longestPath(Node n, int prevNodePath) {

    if (n != null && n.getLeftSon() != null && n.getRightSon() != null) {
        int currNodePath = countLeftNodes(n.getLeftSon()) + countRightNodes(n.getRightSon());
        int leftLongestPath = countLeftNodes(n.getLeftSon().getLeftSon()) + countRightNodes(n.getLeftSon().getRightSon());
        int rightLongestPath = countLeftNodes(n.getRightSon().getLeftSon()) + countRightNodes(n.getRightSon().getRightSon());

        int longestPath = currNodePath > leftLongestPath ? currNodePath : leftLongestPath;
        longestPath = longestPath > rightLongestPath ? longestPath : rightLongestPath;

        longestPath(n.getLeftSon(), longestPath);
        longestPath(n.getRightSon(), longestPath);

        return longestPath > prevNodePath ? longestPath : prevNodePath;
    }
    return 0;
}
private static int countLeftNodes(Node n) {
    if (n != null) {
        return 1+ countLeftNodes(n.getLeftSon());
    }
    return 0;
}
private static int countRightNodes(Node n) {
    if (n != null) {
        return 1+ countRightNodes(n.getRightSon());
    }
    return 0;
}

Я понимаю, что где-то отсутствует ключевая концепция ... Моймозг сходит с ума, когда я пытаюсь отслеживать ход казни ...Прав ли я, говоря, что, найдя самый длинный путь среди корня, его левого и правого узлов, а затем рекурсивно на своих левых и правых узлах, передавая им самый длинный путь из предыдущего вызова метода и, наконец, (когда?) Возвращаю самый длинный путь, яя не уверен, как ты его вернешь ...

Ответы [ 9 ]

14 голосов
/ 26 июня 2010

Может быть, это так же просто:

public static int longestPath(Node n) {
    if (n != null) {
        return longestPath(n, 0); // forgot return?
    }
    return 0;
}

Это сложнее, чем можно подумать с первого взгляда. Рассмотрим следующее дерево:

      1
     / \
    2   3
   / \
  4   5
 / \   \
6   7   8
   / \   \
  9   a   b

В этом случае корневой узел даже не находится на самом длинном пути (a-7-4-2-5-8-b).

Итак, что вы должны сделать, это следующее: Для каждого узла n вы должны вычислить следующее:

  • вычисляет самый длинный путь в левом поддереве, начиная с корня левого поддерева (называемого L)
  • вычисляет самый длинный путь в правом поддереве, начиная с корня правого поддерева (называемого R)
  • вычисляет самый длинный путь в левом поддереве (необязательно начиная с корня левого поддерева) (называемый l)
  • вычисляет самый длинный путь в правом поддереве (необязательно начиная с корня правого поддерева) (называемый r)

Затем решите, какая комбинация максимизирует длину пути:

  • L+R+2, то есть от подпути в левом поддереве к текущему узлу и от текущего узла через подпуть в правом поддереве
  • l, т.е. просто возьмите левое поддерево и исключите текущий узел (и, следовательно, правое поддерево) из пути
  • r, т.е. просто взять правое поддерево и исключить текущий узел (и, следовательно, левое поддерево) из пути

Так что я бы сделал небольшой хак и для каждого узла вернул бы не один int, а тройку целых чисел, содержащих (L+R+2, l, r). Затем вызывающая сторона должна решить, что делать с этим результатом, в соответствии с приведенными выше правилами.

12 голосов
/ 26 июня 2010

Правильный алгоритм:

  1. Запустите DFS из любого узла, чтобы найти самый дальний конечный узел.Маркируйте этот узел T.
  2. Запустите другую DFS, чтобы найти самый дальний узел из T.
  3. Путь, найденный на шаге 2, является самым длинным путем в дереве.

Этот алгоритм определенно будет работать, и вы не ограничены только двоичными деревьями.Я не уверен в вашем алгоритме:

Прав ли я, сказав, что, найдя самый длинный путь среди корня, его левого и правого узлов, а затем рекурсивно на его левом и правом узлах, передавая имсамый длинный путь из предыдущего вызова метода и, наконец, (когда ???) возвращает самый длинный путь, я не уверен, как вы его вернете ...

, потому что я не понимаючто именно вы описываете.Можете ли вы сделать это вручную на примере или попытаться объяснить это лучше?Таким образом, вы можете лучше понять, правильно ли это.

Вы, кажется, пытаетесь рекурсивно реализовать в основном то же самое, что просто упрощено для двоичных деревьев.Ваш код кажется довольно сложным для этой проблемы, однако.Проверьте обсуждение здесь для более простой реализации.

2 голосов
/ 24 сентября 2012

Простая реализация:

int maxDepth(Node root) {
    if(root == null) {
        return 0;
    } else {
        int ldepth = maxDepth(root.left);
        int rdepth = maxDepth(root.right);
        return ldepth>rdepth ? ldepth+1 : rdepth+1;
    }
}

int longestPath(Node root)
{
   if (root == null)
     return 0;

  int ldepth = maxDepth(root.left);
  int rdepth = maxDepth(root.right);

  int lLongPath = longestPath(root.left);
  int rLongPath = longestPath(root.right);

  return max(ldepth + rdepth + 1, max(lLongPath, rLongPath));
}
2 голосов
/ 09 сентября 2012
public int longestPath() {
    int[] result = longestPath(root);
    return result[0] > result[1] ? result[0] : result[1];
}

// int[] {self-contained, root-to-leaf}
private int[] longestPath(BinaryTreeNode n) {
    if (n == null) {
        return new int[] { 0, 0 };
    }
    int[] left = longestPath(n.left);
    int[] right = longestPath(n.right);

    return new int[] { Util.max(left[0], right[0], left[1] + right[1] + 1),
            Util.max(left[1], right[1]) + 1 };
}
2 голосов
/ 19 августа 2012

Вот мое рекурсивное решение в C ++:

int longest_dis(Node* root) {
    int height1, height2;

    if( root==NULL)
        return 0;

    if( root->left == NULL ) && ( root->right == NULL )
        return 0;

    height1 = height(root->left); // height(Node* node) returns the height of a tree rooted at node
    height2 = height(root->right);

    if( root->left != NULL ) && ( root->right == NULL )
        return max(height1+1, longest_dis(root->left) );

    if( root->left == NULL ) && ( root->right != NULL )
        return max(height2+1, longest_dis(root->right) );

    return max(height1+height2+2, longest_dis(root->left), longestdis(root->right) );
}
1 голос
/ 25 января 2013

Принимая во внимание пример @phimuemue и решение @IVlad, я решил проверить это сам, поэтому вот моя реализация решения @IVlad в python:

def longestPath(graph,start, path=[]):
    nodes = {}
    path=path+[start]
    for node in graph[start]:
        if node not in path:
            deepestNode,maxdepth,maxpath = longestPath(graph,node,path)
            nodes[node] = (deepestNode,maxdepth,maxpath)
    maxdepth = -1
    deepestNode = start
    maxpath = []
    for k,v in nodes.iteritems():
        if v[1] > maxdepth:
            deepestNode = v[0]
            maxdepth = v[1]
            maxpath = v[2]
    return deepestNode,maxdepth +1,maxpath+[start]

if __name__ == '__main__':
    graph = { '1' : ['2','3'],
              '2' : ['1','4','5'],
              '3' : ['1'],
              '4' : ['2','6','7'],
              '5' : ['2','8'],
              '6' : ['4'],
              '7' : ['4','9','a'],
              '8' : ['5','b'],
              '9' : ['7'],
              'a' : ['7'],
              'b' : ['8']
    }
    """
          1
         / \
        2   3
       / \
      4   5
     / \   \
    6   7   8
       / \   \
      9   a   b
    """
    deepestNode,maxdepth,maxpath = longestPath(graph,'1')
    print longestPath(graph, deepestNode)

>>> ('9', 6, ['9', '7', '4', '2', '5', '8', 'b'])
1 голос
/ 08 января 2011

Ну, если я правильно понял ваш вопрос, вот мое решение [но в C ++ (извините)]:

int h(const Node<T> *root)
{
    if (!root)
        return 0;
    else
        return max(1+h(root->left), 1+h(root->right));
}

void longestPath(const Node<T> *root, int &max)
{
    if (!root)
        return;
    int current = h(root->left) + h(root->right) + 1;
    if (current > max) {
        max = current;
    }
    longestPath(root->left, max);
    longestPath(root->right, max);
}

int longest()
{
    int max = 0;
    longestPath(root, max);
    return max;
}
1 голос
/ 26 июня 2010

Что если для каждого узла n ваша цель состояла в том, чтобы вычислить эти два числа:

  • f (n): длина самого длинного пути в дереве с корнем в n
  • h (n): высота дерева с корнем в n.

Для каждого конечного узла (узлы, имеющие null левый и правый узлы), очевидно, что f и h оба равны 0.

Теперь h каждого узла n:

  • 0, если n.left и n.right оба null
  • 1 + ч (n.left), если только n.left не является null
  • 1 + h (n.right), если только n.right не является null
  • 1 + макс (ч (n.left), ч (n.right)), если оба n.left и n.right не являются null

А f (n) равно:

  • 0, если n.left и n.right оба null
  • max (f (n.left), h (n)), если только n.left не является null
  • ?? если только n.right не является null
  • ?? если n.left и n.right не являются null

(Вам нужно выяснить, что заменяет два заполнителя "??". Есть варианты, которые заставляют эту стратегию работать. Я лично проверил ее.)

Тогда longestPath(Node n) - это просто f (n):

public class SO3124566
{
    static class Node
    {
        Node left, right;

        public Node()
        {
            this(null, null);
        }

        public Node(Node left, Node right)
        {
            this.left = left;
            this.right = right;
        }
    }

    static int h(Node n)
    {
        // ...
    }

    static int f(Node n)
    {
        // ...
    }

    public static int longestPath(Node n)
    {
        return f(n);
    }

    public static void main(String[] args)
    {
        { // @phimuemue's example
            Node n6 = new Node(),
                n9 = new Node(),
                a = new Node(),
                n7 = new Node(n9, a),
                n4 = new Node(n6, n7),
                b = new Node(),
                n8 = new Node(null, b),
                n5 = new Node(null, n8),
                n2 = new Node(n4, n5),
                n3 = new Node(),
                n1 = new Node(n2, n3);
            assert(longestPath(n1) == 6);
        }{ // @Daniel Trebbien's example: http://pastebin.org/360444
            Node k = new Node(),
                j = new Node(k, null),
                g = new Node(),
                h = new Node(),
                f = new Node(g, h),
                e = new Node(f, null),
                d = new Node(e, null),
                c = new Node(d, null),
                i = new Node(),
                b = new Node(c, i),
                a = new Node(j, b);
            assert(longestPath(a) == 8);
        }



        assert(false); // just to make sure that assertions are enabled.
            // An `AssertionError` is expected on the previous line only.
    }
}

Вы должны быть в состоянии написать рекурсивные реализации f и h, чтобы этот код работал; Однако это решение ужасно неэффективно. Его цель - просто понять расчет.

Чтобы повысить эффективность, вы можете использовать памятка или преобразовать это в нерекурсивный расчет, который использует стек (ы).

1 голос
/ 26 июня 2010

Я думаю, что вы слишком усложняете вещи.

Подумайте о самом длинном пути, проходящем через узел n и не доходящем до родителя n.Какова связь между длиной этого пути и высотой обеих подзадач, соединенных с n?

После выяснения этого проверьте рекурсивное рассуждение дерева следующим образом:

Самый длинный путь дляподдерево с корнем n - это самый длинный путь из следующих трех:

  1. самый длинный путь в поддереве, корнем которого является n.left_child
  2. самый длинный путь в поддереве, чейroot - это n.right_child
  3. Самый длинный путь, который проходит через узел n и не идет к родителю n
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...