簡體   English   中英

2個節點之間的最長路徑

[英]Longest path between 2 Nodes

計算兩個節點之間的最長路徑。
路徑是拱形的。
簽名方法是:

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)的三個整數。 然后,呼叫者必須根據上述規則決定如何處理此結果。

一個正確的算法是:

  1. 從任何節點運行DFS以查找最遠的葉節點。 標記節點T.
  2. 運行另一個DFS以從T找到最遠的節點。
  3. 您在步驟2中找到的路徑是樹中最長的路徑。

這個算法肯定會起作用,你也不僅限於二叉樹。 我不確定你的算法:

我是這么說的,通過找到根中的最長路徑,它的左右節點然后遞歸到它的左右節點,從它們傳遞前一個方法調用的最長路徑,最后(當???)返回最長的路徑,我不確定你是怎么回事的......

因為我不明白你究竟在描述什么。 你可以手工制作一個例子或嘗試更好地解釋它嗎? 這樣你可以更好地幫助理解它是否正確。

您似乎正在嘗試遞歸實現基本相同的事情,只是為二叉樹簡化。 但是,對於這個問題,您的代碼似乎相當復雜 請查看此處的討論以獲得更簡單的實現。

這是我在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) );
}
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));
}

考慮到@phimuemue示例和@IVlad解決方案,我決定自己檢查一下,所以這是我在python中實現的@IVlad解決方案:

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'])

我覺得你太復雜了。

考慮通過節點n的最長路徑並且不會到達n的父節點。 該路徑的長度與連接到n的兩個子區的高度之間的關系是什么?

搞清楚之后,檢查樹遞歸推理如下:

具有根n的子樹的最長路徑是以下三個中最長的路徑:

  1. 子樹中最長的路徑,其根為n.left_child
  2. 子樹中最長的路徑,其根為n.right_child
  3. 最長的路徑,通過節點n並且不會到達n的父節點

如果,對於每個節點n ,您的目標是計算這兩個數字:

  • f( n ):以n為根的樹中最長路徑的長度
  • h( n ):以n為根的樹的高度。

對於每個終端節點(具有null左和右節點的節點),顯然f和h都是0。

現在,每個節點n的h是:

  • 如果n.leftn.right都為nulln.right 0
  • 如果只有n.left為非null n.left 1 + h( n.left
  • 如果只有n.right為非null n.right 1 + h( n.right
  • 1 + max(h( n.left ),h( n.right ))如果n.leftn.right都是非null

f( n )是:

  • 如果n.leftn.right都為nulln.right 0
  • max(f( n.left ),h( n ))如果只有n.left是非null
  • ?? 如果只有n.right是非null
  • ?? 如果n.leftn.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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM