Вычислите самый длинный путь между двумя узлами.
Путь находится в дуге.
Подпись метода:
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;
}
Я понимаю, что пропускаю ключевое понятие где-нибудь... Мой мозг сходит с ума, когда я пытаюсь отследить поток выполнения...
Действительно ли я прав путем высказывания что путем нахождения самого длинного пути среди корня, его левых и правильных узлов и затем рекурсивно вызываю его левые и правильные узлы, передающие их самый длинный путь от предыдущего вызова метода и наконец (когда?) возвращают самый длинный путь, я не уверен относительно того, как Вы идете о возврате его...
Может быть, это так же просто:
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)
. Затем вызывающий должен решить, что делать с этим результатом в соответствии с приведенными выше правилами.
Правильный алгоритм:
Этот алгоритм определенно будет работать, и вы также не ограничены только двоичными деревьями. Я не уверен насчет вашего алгоритма:
Я прав, говоря, что путем нахождения самого длинного пути среди корня, его левого и правого узлов, а затем рекурсии на его левом и правом узлах, передавая им самый длинный путь из предыдущего вызова метода и наконец (когда ???) вернуть самый длинный путь, я не уверен, как вы собираетесь его вернуть ...
потому что я не понимаю, что именно вы описываете. Можете ли вы проработать это вручную на примере или попытаться лучше объяснить? Так вы сможете лучше понять, правильно это или нет.
Похоже, вы пытаетесь рекурсивно реализовать в основном то же самое, только упрощенное для двоичных деревьев. Однако ваш код кажется довольно сложным для этой проблемы. Просмотрите обсуждение здесь для более простой реализации.
Мне кажется, вы слишком усложняете задачу.
Подумайте о самом длинном пути, который проходит через узел n и не поднимается к родителю n. Какая связь между длиной этого пути и высотами обоих поддеревьев, соединенных с n?
Выяснив это, проверьте дерево рекурсивно, рассуждая следующим образом:
Самый длинный путь для поддерева с корнем n - это самый длинный путь из следующих трех:
Что, если для каждого узла n
вашей целью было вычислить эти два числа:
n
): длина самого длинного путь в дереве с корнем n
n
): высота дерева с корнем в n
. Для каждого конечного узла (узлов, имеющих нулевой
левый и правый узлы), очевидно, что f и h оба равны 0.
Теперь h каждого узла n
] равно:
n.left
и n.right
оба равны null
n.left
) если только n.left
не- null
n.right
), если только n.right
не- null
n.left
), h ( n.right
)), если оба n.left
и ] n.right
не - null
И f ( n
) равно:
n.left
и n. right
оба null
n.left
), h ( n
)), если только n.left
не - null
п.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, чтобы этот код работал; однако это решение ужасно неэффективно. Его цель - просто понять расчет.
Чтобы повысить эффективность, вы можете использовать мемоизацию или преобразовать это в нерекурсивное вычисление, использующее стек (ы).