簡體   English   中英

總是向左|向右的樹中下降路徑的最大長度

[英]maximum length of a descending path in a tree which always goes left|right

我正在為技術面試做准備,所以基本上從一開始就學習算法:) 我們獲得了 BST。 我需要在其中找到一條 desc 路徑的最大長度,它總是向左或向右 換句話說,示例樹的降序路徑為 2,即 15-10-6

   5
  / \
2     15
     /
    10
   / \ 
  6   14

我對算法問題很陌生。我解決這個問題的步驟是什么?

我的想法是使用 DFS 和計數器來存儲最長路徑。 但我不知道如何使用遞歸來完成這項工作,而遞歸對於這種數據結構來說似乎更自然。

任何建議/方向?

措辭有點混亂,但我認為你的意思是最大的

  • 從任何節點開始然后只向左走的路徑的最大長度,或
  • 從任何節點開始然后僅向右走的路徑的最大長度。

您分兩次完成此操作,一次查找最大左側路徑,另一次查找最大右側路徑(然后取兩者中的最大值)。 或者您可以一次完成,同時完成這兩項工作。

對於每個節點,您想知道三個值:

  1. 從該節點開始的左路徑的長度,
  2. 從該節點開始的正確路徑的長度,以及
  3. 從該節點或其后代之一開始的最長路徑的長度。

如果您以遞歸方式執行此操作,這意味着遞歸應返回這三個值,可能作為一個小數組或作為一個簡單的三字段對象。

這看起來像

Results calculate(Tree node) {
    if (node == null) return new Results(0,0,0);
    else {
        Results leftResults = calculate(node.left);
        Results rightResults = calculate(node.right);
        int leftLength = 1 + leftResults.leftLength;
        int rightLength = 1 + rightResults.rightLength;
        int maxLength = Math.max(Math.max(leftLength, rightLength), 
                                 Math.max(leftResults.maxLength, rightResults.maxLength));
        return new Results(leftLength, rightLength, maxLength);
    }
}

並且整體結果將只是calculate(root).maxLength

非遞歸解決方案

實際上,這是我測試過的 Codibility 上的一個問題。 我只是為了討論它而提到一個非遞歸解決方案。

樹本身有一個可以修改的值。

我在這里找到了一個比遞歸解決方案更好的解決方案,但我沒有用 Java 編程,所以我將把 C# 解決方案放在正確的算法上:

public class Tree
{
    public int x;
    public Tree l;
    public Tree r;
}
class solution
{
    public int solution(Tree T)
    {
        // write your code in C# 5.0 with .NET 4.5 (Mono)
        List<Tree> toProcess = new List<Tree>(10000);

        if (T == null)
            return 0;
        int maxLength = 0;
        T.x = 0;
        toProcess.Add(T);

        while (toProcess.Count != 0)
        {
            Tree currNode = toProcess[toProcess.Count-1];
            toProcess.RemoveAt(toProcess.Count - 1);
            int remainder = currNode.x % 100000;
            if (currNode.l != null)
            {
                currNode.l.x = 1 + remainder;
                maxLength = Math.Max(maxLength, currNode.l.x);
                toProcess.Add(currNode.l);
            }
            if (currNode.r != null)
            {
                currNode.r.x = 100000 + (currNode.x - remainder);
                maxLength = Math.Max(maxLength, currNode.r.x / 100000);
                toProcess.Add(currNode.r);
            }
        }
        return maxLength;
    }
}

如果你計時,這比通過倍數回避要快。 這個想法是在每個節點上,您在子節點中存儲更長的長度並將它們附加到列表(如果需要,您可以使用堆棧)以供稍后處理。 您使用 int 來存儲計數。 Codibility 上的原始問題提到不超過 10 000 個節點,最大深度為 800。

最后一個優化是將我使用的 100000 替換為二進制移位來分隔左右長度,這會更快,即使用前 16 位存儲左邊的長度,其余的存儲右邊的長度,但我沒有當我開始使用遞歸方法時,有足夠的時間來做到這一點。

編輯:我做了按位,太糟糕了我沒有時間確保它是正確的並提交它,因為它比遞歸快得多:

    public int solution(Tree T)
    {
        // write your code in C# 5.0 with .NET 4.5 (Mono)
        List<Tree> toProcess = new List<Tree>(10000);
        
        int rightmask = 0x0000FFFF;
        int leftmask = ~0x0000FFFF;
        if (T == null)
            return 0;
        int maxLength = 0;
        T.x = 0;
        toProcess.Add(T);

        while (toProcess.Count != 0)
        {
            Tree currNode = toProcess[toProcess.Count-1];
            toProcess.RemoveAt(toProcess.Count - 1);
            
            if (currNode.l != null)
            {
                int leftpart = (currNode.x & leftmask) >> 16;
                int newLength = 1 + leftpart;
                currNode.l.x = newLength << 16;
                maxLength = Math.Max(maxLength, newLength);
                toProcess.Add(currNode.l);
            }
            if (currNode.r != null)
            {
                int rightpart = (currNode.x & rightmask);
                currNode.r.x = 1 + rightpart;
                maxLength = Math.Max(maxLength, currNode.r.x);
                toProcess.Add(currNode.r);
            }
        }
        return maxLength;
    }

主意:

從節點v調用的遞歸函數應返回 3 個值:

1. Maximum descending path which goes always left or always right in subtree rooted in v

2. Maximum length of path which goes always left starting from v

3. Maximum length of path which goes always right starting from v

讓我們分別稱這些值(V1, V2, V3)

基本情況:

顯然,對於樹中的任何葉子,上述所有值都等於 0。

遞歸調用:

讓我們考慮任何內部節點v

(L1, L2, L3)是遞歸調用v左子節點返回的值。

(R1, R2, R3)為遞歸調用v右孩子所返回的值。

然后v ,為了計算(V1, V2, V3)只需要組合從左孩子和右孩子返回的結果:

如果存在左孩子,則V2等於L2 + 1 否則為 0。

如果存在右孩子,則V3等於R3 + 1 否則為 0。

V1等於max(L1, R1, V2, V3)

這是問題的工作代碼。 給出一個 11 節點的二叉樹用於測試:

公共類節點{

int L = 0;
int R = 0;

Node left = null;
Node right = null;

}

公共類 BTree {

void calculate_paths(Node root) {

    if (root.left != null) {
        calculate_paths(root.left);
        root.L = root.left.L + 1;
    }

    if (root.right != null) {
        calculate_paths(root.right);
        root.R = root.right.R + 1;
    }
}

int max_paths(Node root) {

    int maxL = 0;
    int maxR = 0;

    if (root.left != null) maxL = max_paths(root.left);
    if (root.right != null) maxR = max_paths(root.right);

    return Math.max(Math.max(root.L, root.R), Math.max(maxL, maxR));

}

}

公共課主要{

public static void main(String[] args){
    System.out.println("Let the challenge begin!");

    //create the tree
    Node n0 = new Node();
    Node n1 = new Node();
    Node n2 = new Node();
    Node n3 = new Node();
    Node n4 = new Node();
    Node n5 = new Node();
    Node n6 = new Node();
    Node n7 = new Node();
    Node n8 = new Node();
    Node n9 = new Node();
    Node n10 = new Node();

    n0.right = n1;
    n0.left = n7;

    n1.left = n2;

    n2.left = n3;
    n2.right = n4;

    n4.right = n5;

    n5.right = n6;

    n6.left = n9;
    n6.right = n10;

    n7.left = n8;


    BTree Tree = new BTree();

    Tree.calculate_paths(n0);
    System.out.println(Tree.max_paths(n0));

}

}

Java實現:

    // auxiliary method, starts the recursion:    

    public int height(){
        if(root != null) // If not null calculate height
            return height(root);
        else // height is zero...
            return 0;
    }

    // Gets the job done:
    private int height(BinaryTreeNode<T> node){

        int right = 0, left = 0;

        if (node.getLeftChild() != null) // count and calculate left path height
            left= 1+ height(node.getLeftChild()); 
        if (node.getRightChild() != null) // count and calculate right path height
            right= 1 + height(node.getRightChild());

        return Math.max(left, right);
    }

暫無
暫無

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

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