【问题标题】:SalesPath - calculating cheapest pathSalesPath - 计算最便宜的路径
【发布时间】:2019-01-07 10:32:17
【问题描述】:

我有一棵树(代表一个销售路径),每个节点都有一个成本。

Object 是计算最小的 SalesPath 即每个 Node 成本的总和 从根节点到叶节点

下面的代码使用递归来解决问题,代码工作正常 (SalesPath (0-3-2-1-1) 在所示树中具有最便宜的路径 = 7)

我在可视化堆栈以及阐明/解释递归如何能够跟踪最便宜的成本时遇到了麻烦 (即int最便宜=最便宜的成本+rootNode.cost)在多个分支之间

例如。从 0 - 5 - 4,最便宜的成本是 9 而从 0 - 3 -2 -1 -1,最便宜的成本是 7

任何人都可以帮助提供简单的解释吗?

代码:

 static class SalesPathRecursion{

            static int getCheapestCost_Recursion(Node rootNode){

                int cheapestCost = Integer.MAX_VALUE;
                Node[] children = rootNode.children;

                int tempCost =0;

                if(children == null){
                    System.out.println(" Children == null, returning rootNode.cost => " + rootNode.cost);
                    return rootNode.cost;
                }else{
                    for(int i=0; i< children.length; i++){
                        tempCost = getCheapestCost_Recursion(children[i]);

                        if(tempCost < cheapestCost){
                            cheapestCost = tempCost;
                        }
                        System.out.println(" CheapestCost is " + cheapestCost);
                    }
                }

                int cheapest = cheapestCost + rootNode.cost;
                System.out.println(" Returning : rootNode.cost => " + rootNode.cost + " cheapest is => " + cheapest);

                return cheapest;
            }
        }

【问题讨论】:

    标签: recursion tree


    【解决方案1】:

    这是递归的经典示例!

    调用者调用递归函数,传入根节点并询问“叶子最便宜的成本是多少?”。调用树底部的根节点 (0) 响应:“我还不确定。等一下,我让我的每个孩子看看哪个孩子的成本最低”。然而,目前还没有一个孩子回答这个问题,所以他们问每个孩子完全相同的问题,依此类推。

    最终,到达leaf node(例如,您的示例中的 4)并说“我知道!从我到叶节点的最便宜的成本是 我自己的价值,4!我’我要告诉我的父母。”这称为base case。基本情况是迄今为止第一个提供明确答案的函数调用,在这种情况下是针对一个非常小的子问题,其中的信息将用于构建更大问题的解决方案。将太大而无法立即解决的问题分解为易于解决的小问题的过程称为divide and conquer

    节点 4 的父节点,节点 5,现在知道从自己到叶子(4,它的唯一子节点)的最便宜的成本是 5 + 4 = 9,并告诉它的父节点,即根节点 0。根节点现在已武装从它最左边的孩子那里得到一个确定的价值(从根到叶的路径成本,9,比无穷大便宜,所以它被存储为迄今为止最好的),但仍然需要向其他孩子询问他们的成本,它确实.最终,所有孩子都会报告他们各自的费用,0-&gt;3-&gt;2-&gt;1-&gt;1 证明是最便宜的。

    该算法使用depth-first search,在探索其他分支之前先完全探索一个分支。也就是说,0-&gt;5-&gt;40-&gt;3-&gt;2-&gt;1-&gt;1 之前被完全探索过了。


    示例 1

    考虑一个具有唯一成本的小节点树示例:

            0
           / \
          4   1
         /  
        5  
    

    我们预计0-&gt;1 是成本为 1 的最便宜路径。让我们在这棵树上运行带有详细对话输出的算法。每当进行递归调用时,打印都会缩进。括号是指一个特定的节点。看看你是否可以追踪执行:

    (0), what's your min cost?
    (0) says: Hang on, I need to ask my children...
    (0) says: I'm asking (4) for its min cost...
          (4), what's your min cost?
          (4) says: Hang on, I need to ask my children...
          (4) says: I'm asking (5) for its min cost...
                (5), what's your min cost?
                (5) says: I'm a leaf! My min cost is 5
          (4) says: I got the cost 5 from (5), a new best!
          (4) says: I checked my children and I know my min cost is 9
    (0) says: I got the cost 9 from (4), a new best!
    (0) says: I'm asking (1) for its min cost...
          (1), what's your min cost?
          (1) says: I'm a leaf! My min cost is 1
    (0) says: I got the cost 1 from (1), a new best!
    (0) says: I checked my children and I know my min cost is 1
    

    示例 2

    这是另一棵更大的树,同样具有唯一节点:

             0
           / | \
          4  1  7
         /  / \  
        5  6   2
                \
                 3
    

    我们预计0-&gt;1-&gt;2-&gt;3 是成本为 6 的最便宜路径。让我们看看详细输出。再次尝试跟踪执行。

    (0), what's your min cost?
    (0) says: Hang on, I need to ask my children...
    (0) says: I'm asking (4) for its min cost...
          (4), what's your min cost?
          (4) says: Hang on, I need to ask my children...
          (4) says: I'm asking (5) for its min cost...
                (5), what's your min cost?
                (5) says: I'm a leaf! My min cost is 5
          (4) says: I got the cost 5 from (5), a new best!
          (4) says: I checked my children and I know my min cost is 9
    (0) says: I got the cost 9 from (4), a new best!
    (0) says: I'm asking (1) for its min cost...
          (1), what's your min cost?
          (1) says: Hang on, I need to ask my children...
          (1) says: I'm asking (6) for its min cost...
                (6), what's your min cost?
                (6) says: I'm a leaf! My min cost is 6
          (1) says: I got the cost 6 from (6), a new best!
          (1) says: I'm asking (2) for its min cost...
                (2), what's your min cost?
                (2) says: Hang on, I need to ask my children...
                (2) says: I'm asking (3) for its min cost...
                      (3), what's your min cost?
                      (3) says: I'm a leaf! My min cost is 3
                (2) says: I got the cost 3 from (3), a new best!
                (2) says: I checked my children and I know my min cost is 5
          (1) says: I got the cost 5 from (2), a new best!
          (1) says: I checked my children and I know my min cost is 6
    (0) says: I got the cost 6 from (1), a new best!
    (0) says: I'm asking (7) for its min cost...
          (7), what's your min cost?
          (7) says: I'm a leaf! My min cost is 7
    (0) says: I got the cost 7 from (7) but it's not the best
    (0) says: I checked my children and I know my min cost is 6
    

    这是产生上述输出的代码:

    static void say(String message, int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print(" ");
        }
    
        System.out.println(message);
    }
    
    static int getCheapestCostRecursion(Node node, int depth) {
        say("(" + node.cost + "), what's your min cost?", depth);
    
        if (node.children.length == 0) {
            say(
                "(" + node.cost + 
                ") says: I'm a leaf! My min cost is " +
                node.cost, depth
            );
            return node.cost;
        }
    
        say(
            "(" + node.cost + 
            ") says: Hang on, I need to ask my children...",
            depth
        );
    
        int cheapestCost = Integer.MAX_VALUE;
    
        for (Node child : node.children) {
            say(
                "(" + node.cost + 
                ") says: I'm asking (" + child.cost + 
                ") for its min cost...", depth
            );
    
            int childCost = getCheapestCostRecursion(child, depth + 6);
    
            say(
                "(" + node.cost + 
                ") says: I got the cost " + childCost + 
                " from (" + child.cost + ")", depth
            );
    
            if (childCost < cheapestCost) {
                say(
                    "(" + node.cost + 
                    ") says: I got the cost " + childCost + 
                    " from (" + child.cost + "), a new best",
                    depth
                );
                cheapestCost = childCost;
            }
            else {
                say(
                    "(" + node.cost + 
                    ") says: I got the cost " + childCost + 
                    " from (" + child.cost + 
                    "), but it's not the best", depth
                );
            }
        }
    
        say(
            "(" + node.cost + 
            ") says: I checked my children and I know " +
            "my min cost is " + (cheapestCost + node.cost),
            depth
        );
    
        return cheapestCost + node.cost;
    }
    

    Try it!

    【讨论】:

    • 谢谢,这是一个非常有用/详细的解释.. 但是,有一个问题 - 你为什么使用“深度”(最初是 0,然后是深度 + 6)来打印出 cmets?这有什么意义吗?
    • 是的——这决定了你在输出中看到的缩进级别。每个调用深度进一步缩进 6 个空格,因此您可以轻松可视化调用堆栈。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    • 1970-01-01
    • 2017-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多