【问题标题】:(java) weird List value in DFS traversal(java) DFS 遍历中奇怪的 List 值
【发布时间】:2017-08-02 03:36:28
【问题描述】:

我试图解决一个问题:

打印二叉树的所有路径,从根到叶。

我写了如下代码,结果是正确的:

public void printPath(TreeNode root) {

    printDFS(root, new int[1000], 0);

}



private void printDFS(TreeNode r, int[] path, int idx){
    if (r == null) return;

    path[idx] = r.val;
    idx++;

    /* if it's leaf, print path: from root to leaf */
    if (r.left == null && r.right == null) 
        printOnePath(path, idx);

    else {
        /* go left, go right */
        printDFS(r.left,  path, idx);
        printDFS(r.right, path, idx);
    }
}


private void printOnePath(int[] path, int idx) {
    for (int i = 0; i < idx; i++) {
        System.out.print(path[i] + " ");
    }
    System.out.println();         
}

然而,
当我尝试使用 ArrayList 来存储路径数据时,而不是 int[]
这种方法变得错误。
而且输出结果真的让我迷失了。

public void printPath(TreeNode root) {

    printDFS(root, new ArrayList<Integer>(), 0);        

}

private void printDFS(TreeNode r, List<Integer> path, int idx){
    if (r == null) return;

    path.add( r.val );
    idx++;

    /* if it's leaf, print path: from root to leaf */
    if (r.left == null && r.right == null) 
        printOnePath(path, idx);

    else {
        /* otherwise: go left, go right */
        printDFS(r.left,  path, idx);
        printDFS(r.right, path, idx);
    }
}


private void printOnePath(List<Integer> path, int idx) {
    for (int i = 0; i < idx; i++) {
        System.out.print(path.get(i) + " ");
    }
    System.out.println();         
}

例如:

对于二叉树:

第一个 STDOUT 是:(正确) 10 5 3 3 10 5 3 -2 10 5 2 1 10 -3 11

第二个 STDOUT 是:(错误) 10 5 3 3 10 5 3 3 10 5 3 3 10 5 3

我相信初始的 ArrayList 在每个 DFS 的一开始就已经设置为空。 为什么结果与 int 数组完全不同,即使使用相同的方法?

有人知道为什么吗?非常感谢!

【问题讨论】:

  • path[idx] = r.val;path.add( r.val ); 的含义截然不同。
  • ArrayList类包含所有元素,包括null,并且列表不能存储double等原始值。
  • @Tehmina 那么你的意思是什么?该代码已经使用了ArrayList&lt;Integer&gt;,它可以工作。

标签: java arraylist tree traversal microsoft-distributed-file-system


【解决方案1】:

我遵循 ajb 提到的复制策略(此评论中的选项 #2)。这是修改后的代码。

    import apple.laf.JRSUIUtils;

    import java.util.ArrayList;
    import java.util.List;

    /**
     * Created by anilwaddi on 8/1/17.
     */
    public class DFSTest {


      public static void main(String args[]) throws Exception {

        DFSTest test = new DFSTest();
        test.printDFS();
    /*
    10 5 3 3
    10 5 3 -2
    10 5 2 1
    10 -3 11
     */
      }

      TreeNode root;

      DFSTest() {
        TreeNode node41 = new TreeNode(3, null, null);
        TreeNode node42 = new TreeNode(-2, null, null);
        TreeNode node43 = new TreeNode(1, null, null);


        TreeNode node31 = new TreeNode(3, node41, node42);
        TreeNode node32 = new TreeNode(2, node43, null);
        TreeNode node33 = new TreeNode(11, null, null);

        TreeNode node21 = new TreeNode(5, node31, node32);
        TreeNode node22 = new TreeNode(-3, node33, null);
        root = new TreeNode(10, node21, node22);
      }

      public void printDFS() {
        printPath(root);
      }

      public void printPath(TreeNode root) {
        printDFS(root, new ArrayList<Integer>());

      }

      private void printDFS(TreeNode r, List<Integer> path ) {
        if (r == null) return;

        path.add(r.val);

        /* if it's leaf, print path: from root to leaf */
        if (r.left == null && r.right == null)
          printOnePath(path );

        else {
            /* otherwise: go left, go right */
          List<Integer> newPathLeft = new ArrayList<>();
          newPathLeft.addAll(path);
          printDFS(r.left, newPathLeft);

          List<Integer> newPathRight = new ArrayList<>();
          newPathRight.addAll(path);
          printDFS(r.right, newPathRight);
        }
      }


      private void printOnePath(List<Integer> path ) {
        for (int i = 0; i < path.size(); i++) {
          System.out.print(path.get(i) + " ");
        }
        System.out.println();
      }

      private class TreeNode {
        TreeNode left;
        TreeNode right;
        Integer val;

        TreeNode(Integer val) {
          this.val = val;
        }

        TreeNode(Integer val, TreeNode left, TreeNode right) {
          this.val = val;
          this.left = left;
          this.right = right;
        }
      }
    }

【讨论】:

    【解决方案2】:

    区别在于idx,一个int,是按值传递的。但是,ArrayListpath 是一个对象;因此,虽然作为引用的参数是按值传递的,但path 引用的对象不是按值传递的,而是在递归方法的所有调用之间共享。

    这意味着在你的第一个方法中,当你递归调用它时:

        printDFS(r.left,  path, idx);
        printDFS(r.right, path, idx);
    

    假设您在r.left 上调用printDFSidx 为3。当递归方法将idxidx++ 递增时,它们正在递增idx 的新副本。因此,当递归方法最终结束并返回时,该方法正在使用的idx 的副本仍然是3,因此它将调用printDFS(r.right, path, 3)。这会有所不同,因为您使用idx 来设置path[idx]

    在您的第二种方法中,idx 的行为方式相同,但您在构建 ArrayList 时并未使用它。相反,您添加到ArrayList。所以当你打电话时

        printDFS(r.left,  path, idx);
        printDFS(r.right, path, idx);
    

    现在,如果 path 有 3 个元素,当您在 r.left 上调用 printDFS 时,当递归调用结束并返回时,它们已添加到 ArrayList--并且由于该对象在所有人之间共享递归调用,path 仍将包含递归调用添加到其中的所有元素。因此,与第一个代码示例不同的是,您调用 printDFS(r.right...) 的值与调用 printDFS(r.left...) 的值不同。

    有几种方法可以解决这个问题:

    1. 使用idx 设置数组元素,正如另一个答案所建议的那样。不过,您需要检查idx 是否是现有元素的索引,如果元素存在则使用set,如果idx 等于path.size(),则使用add

    2. 在调用每个printDFS之前复制path。这确保所有递归调用都有自己的数组的干净副本。 (或者在printDFS 的开头复制一份并使用它而不是原始路径;这应该确保没有printDFS 修改其调用者的path。)

    3. 确保每个printDFS 离开path 的方式与其找到它的方式相同。因此,由于printDFS 在数组末尾添加了一个新元素,因此在返回之前使其删除数组的最后一个元素。 (我相信这会起作用,虽然我还没有测试过。但是,我一般不推荐这种方法;对于更复杂的递归情况,正确地“清理”可能非常困难。)

    【讨论】:

      【解决方案3】:

      线条:

      path[idx] = r.val;
      

      path.add(r.val);
      

      不等价。

      结果是第一个路径之后的后续路径被添加到列表的末尾。当您打印path 时,这些不会被看到,因为您最多只能打印idx 元素。

      我不相信在 Java 中存在与数组赋值直接等效的 List。

      打电话

      path.set(idx, r.val);
      

      不起作用,因为 Java 不会自动增长列表。

      打电话

      path.add(idx, r.val);
      

      也不起作用,因为它将现有元素向右移动。

      如果您想使用列表,我认为您必须在打印后将其清除。

      【讨论】:

      • path.set(idx, r.val) 不等同于path[idx] = r.val,因为set 仅在索引已存在于path 中时才有效。您永远不能使用setArrayList 添加新值并使其更长。
      • 还是不行。使用两个参数add 不像set 那样工作;它将插入元素。获得此权利的唯一方法是使用if,在列表末尾使用add,或使用set,具体取决于idx 是否等于列表大小。
      猜你喜欢
      • 1970-01-01
      • 2016-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-09
      • 1970-01-01
      • 2015-03-25
      • 1970-01-01
      相关资源
      最近更新 更多