【问题标题】:How is Backtracking done using recusrion in Binary Tree如何在二叉树中使用递归完成回溯
【发布时间】:2014-04-14 22:08:18
【问题描述】:

我正在尝试插入一个二进制节点。我的代码很复杂,没有挽救它的希望,所以我打算重写它(基本上我没有考虑回溯,也没有仔细考虑算法)。

我正在尝试使用顺序遍历插入二进制节点,但我不明白我应该如何回溯。

     D
    / \
   B   E 
  / \ / \
 A  C F 

如何搜索根 D 的左子树,然后返回并搜索右子树?这可能是一个愚蠢的问题,但我很难过。我能想到的最好的是这样的:

 if (!root.hasLeftChild) {
      root = root.getLeftChild(); 
      recurse(root); 
 } 

但是当我到达底部时,我无法回到根部。此外,它不能解决如果我到达左下角节点我必须在开始回溯之前填满该节点的两个子节点的问题。

我想我想错了。

【问题讨论】:

    标签: java recursion binary-tree


    【解决方案1】:

    树:

         D
        / \
       B   E 
      / \ / \
     A  C F 
    

    对于递归:

    使用 InOrder 遍历

      if (root == null) 
        return;
    
          recurse(root.getLeftChild()); 
          int rootData = root.getData();
          recurse(root.getRightChild()); 
    

    现在,它是如何递归的:

    root.getLeftChild() 将继续递归调用左子子,除非它命中 null 节点(所以控制不会转到 recurse(root.getRightChild());,除非 null 节点被命中

    树走了这么远:

             D
            / 
           B    
          / 
         A  
        /
      null <-- current Position
    

    所以一旦它到达节点AA.left == null,所以它递归回node A(将节点的值分配给rootData

         D
        / 
       B
      / 
     A  <-- current Position
    

    在此之后,它进入下一个递归调用,recurse(root.getRightChild());

             D
            / 
           B    
          / 
         A  
          \
          null <-- current Position
    

    现在,由于A 中的leftright 已被遍历,控制将返回到节点B(称为b.left = A)并寻找B.right


    以这个 Stack 为例,对于下面的树 ( node , value )

         A
        / \
       B   E 
      / \ / \
     C  D F 
    

    步骤:

    • A 调用它的左边 B,所以 A 被压入堆栈
    • B 调用它的左边 C,所以 B 入栈
    • C 调用它的左边 null,所以 C 被压入堆栈
    • 现在 C 将 leftright 作为 null...所以这标志着该节点的递归结束,因此它的 popped 出栈
    • 现在,C 已被完全遍历,因此,当 B 调用 C 时,控制权返回到 B 并检查哪一行调用了此命令
    • b.left被执行时,它现在将转到B.rightpush D......这样下去,这叫做递归栈

    【讨论】:

    • 好吧,我对它如何进行下一个递归调用有点困惑。当root == null时,方法返回,为什么一直这样?
    • 我认为你需要研究递归堆栈...当一个程序被递归调用时,每个被调用的子程序都会被操作到一个新的内存中stack...一旦当前堆栈操作完成,控件返回到调用刚刚完成堆栈的点
    • 啊,我以前从来没有真正使用过递归堆栈,但我的教授在讲座中确实尝试过解释它。我看了一个 YouTube 视频,它解释得更清楚了。所以基本上,堆栈看起来像 DBA,然后它命中 null 并弹出 A 并从 int rootData = root.getData(); 解析。并递归 getRightChild。由于没有什么可做的,它返回 B 并做同样的事情,然后返回 A 并在右侧再次运行它,然后再次尝试获取 leftChild 并运行方法跨度>
    • 其实还有一件事。为什么A是最后一个?当 D 通过该方法时,两个孩子都为 null,所以不应该也将其弹出吗?所以它看起来像:ABD -> AB -> A -> E -> EF -> E -> G?
    • 请注意BB-&gt;C-&gt;null-&gt;C popped off -&gt; back to B -&gt; D -&gt; null -&gt; D popped off -&gt; back to B -&gt; B popped off 的递归是如何发生的……因为,一旦两个孩子都被访问过,那么只有父母被弹出……在树中,如果你曾经有过疑惑,只要了解最后一个父节点和叶子是如何操作的,它会帮助你更好地理解!! :)
    【解决方案2】:

    因此,您正在尝试进行深度优先搜索 - 您可以在任意数量的书籍或维基百科中找到它。

    本质上,它们的关键在于您的函数在每个孩子身上递归地调用自己。例如:

    public Node findSpot(Node node, List<Node> visits){
        visits.add(node);
        //condition check, return this node if its the right node.
        Node result = null;
        for(Node child : node.getListChildren()){
            if((result findSpot(child)) != null){
                 return result
            }
        }
        return null;
    }
    

    所以这递归地检查节点,为树中的每一层深度在堆栈上放置一个新方法。然后它检查下一个分支,如果它没有找到它正在寻找的东西。访问列表将让您查看它访问节点的顺序,以便您查看。这将帮助您了解它的工作原理。

    【讨论】:

      【解决方案3】:

      试试这个例子。它使用递归按顺序访问所有节点:

      public class Example {
      
          public static void main(String[] args) {
              visitInOrder(new Node("D",
                  new Node("B", new Node("A"), new Node("C")),
                  new Node("F", new Node("E"))));
          }
      
          public static void visitInOrder(Node node) {
              if (node != null) {
                  visitInOrder(node.left());
                  System.out.println(node.name());
                  visitInOrder(node.right());
              }
          }
      }
      
      class Node {
      
          private final String name;
          private Node left;
          private Node right;
      
          public Node(String name) {
              this(name, null);
          }
      
          public Node(String name, Node left) {
              this(name, left, null);
          }
      
          public Node(String name, Node left, Node right) {
              this.name = name;
              this.left = left;
              this.right = right;
          }
      
          public String name() { return name; }    
      
          public Node left() { return left; }
      
          public Node right() { return right; }
      }
      

      输出:

      A
      B
      C
      D
      E
      F
      

      【讨论】:

      • 您的方法visitInOrder 实际上是在进行预购访问,但您的结果输出是预期的有序结果。更好地修复方法。
      • @Seelenvirtuose 是的,你是对的。现在我修复了订单,但错过了修复输出。会马上做。谢谢!
      • Ehhmmm:你误会我了!输出 已经正确。它有序的。只是方法不对。现在你修复了方法,但也完全搞砸了输出。现在您必须将输出更改回初始形式。
      • 但是in-order 是:左,自我,右。输出没问题,但完全令人困惑。确认的答案已经抓住了解决方案。我认为我的输出是正确的,不是吗?
      • 完全正确:左、我、右!对于具有父节点“B”、左子节点“A”和右子节点“C”的子树(如问题所示),有序输出为“ABC”。 B 作为当前节点 - 当访问它时 - 在 A 和 C 之间 in !此外,我没有查看您的答案是否是该问题的正确答案。我只看到,您发布了一个按顺序输出,但建议了一个预购方法。目前,您有一个有序的方法和一个既不是 in-order 也不是 pre-order 或 post-order 的输出。搞砸了示例的有序输出将是排序后的输出。
      猜你喜欢
      • 2013-06-05
      • 2013-01-26
      • 2016-04-19
      • 2014-03-29
      • 1970-01-01
      • 2011-02-15
      • 1970-01-01
      • 1970-01-01
      • 2010-12-07
      相关资源
      最近更新 更多