Самый длинный путь между 2 узлами
рассчитать самый длинный путь между двумя узлами.
Путь лежит под аркой.
Подпись метода:
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 ответов
может быть, это так же просто:
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
, т. е. просто возьмите правое поддерево и исключите текущий узел (и, следовательно, левое поддерево) из пути
поэтому я бы сделал немного hack, и для каждого узла не вернуться всего один int
, но тройка целых чисел, содержащий (L+R+2, l, r)
. Затем абонент должен решать, что делать с этим результатом в соответствии с вышеуказанными правилами.
правильный алгоритм:
- запустите DFS с любого узла, чтобы найти самый дальний листовой узел. Метки этого узла т.
- запустите другой DFS, чтобы найти самый дальний узел от T.
- путь, который вы нашли на Шаге 2, является самым длинным путем в дереве.
этот алгоритм определенно будет работать,и вы не ограничены только двоичными деревьями. Я не уверен в вашем алгоритме:
Я прав, говоря, что, найдя самый длинный путь среди корня, его левых и правых узлов, а затем рекурсивно на его левых и правых узлах, передавая им самый длинный путь из предыдущего вызова метода и, наконец, (когда???) верните самый длинный путь, я не уверен, как вы собираетесь его вернуть...
потому что я не понимаю, что именно ты описываешь. Можете ли вы работать с ним вручную на примере или попытаться объяснить его лучше? Таким образом, вы можете получить лучшую помощь в понимании, если это правильно или не.
вы, похоже, пытаетесь рекурсивную реализацию в основном того же самого, что просто упрощено для двоичных деревьев. Ваш код кажется довольно сложным для этой проблемы. Проверьте обсуждение здесь для более простой реализации.
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 };
}
Простая Реализация:
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));
}
вот мое рекурсивное решение на 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) );
}
Я думаю, что вы все усложняете.
подумайте о самом длинном пути, который проходит через узел n и не доходит до родителя n. Какова связь между длиной этого пути и высотами обеих подсетей, связанных с n?
после выяснения этого проверьте дерево рекурсивно рассуждая так:
самый длинный путь для поддерева с корнем n является самым длинным путем из следующих трех:
- в самый длинный путь в поддереве, корень которого n.left_child
- самый длинный путь в поддереве, корнем которого является n.right_child
- самый длинный путь, который проходит через узел n и не доходит до родителя n
что делать, если для каждого узла n
, вашей целью было вычислить эти два числа:
- f (
n
): длина самого длинного пути в дереве с корнем вn
- h (
n
): высота дерева, которое коренится вn
.
для каждого терминального узла (узлов, имеющих null
левый и правый узлы), очевидно, что f и h оба равны 0.
теперь, h каждого узла n
- это:
- 0 если
n.left
иn.right
какnull
- 1 + h (
n.left
) если толькоn.left
неnull
- 1 + h (
n.right
) если толькоn.right
неnull
- 1 + max (h (
n.left
), h (n.right
)) если обаn.left
иn.right
неnull
и f (n
) составляет:
- 0, если
n.left
иn.right
какnull
- max (f (
n.left
), h (n
)) если толькоn.left
is неnull
- ?? если бы только!--9--> не
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, чтобы этот код работал; однако это решение ужасно неэффективно. Его цель - просто понять расчет.
для повышения эффективности, вы можете использовать memoization или преобразуйте это в нерекурсивный расчет, который использует стек(ы).
Ну, Умм, если я правильно понял ваш вопрос, вот мое решение [но на 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;
}
принимая во внимание пример @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'])