【问题标题】:Java recursion: pass by referenceJava递归:通过引用传递
【发布时间】:2011-05-02 21:50:04
【问题描述】:

我意识到这对于 Java 程序员来说是一个激烈争论、有争议的话题,但我相信我的问题有些独特。我的算法 REQUIRES 通过引用传递。我正在对一般树(即n-children)进行顺时针/逆时针前序遍历以分配虚拟(x,y)坐标。这仅仅意味着我在访问它们时计算(并标记)我访问的树的节点。

/**
 * Generates a "pre-ordered" list of the nodes contained in this object's subtree
 * Note: This is counterclockwise pre-order traversal
 * 
 * @param clockwise set to true for clockwise traversal and false for counterclockwise traversal
 * 
 * @return Iterator<Tree> list iterator
 */
public Iterator<Tree> PreOrder(boolean clockwise)
{
    LinkedList<Tree> list = new LinkedList<Tree>();
    if(!clockwise)
        PreOCC(this, list);
    else
        PreO(this,list);
    count = 0;
    return list.iterator();
}
private void PreOCC(Tree rt, LinkedList<Tree> list)
{
    list.add(rt);
    rt.setVirtual_y(count);
    count++;
    Iterator<Tree> ci = rt.ChildrenIterator();
    while(ci.hasNext())
        PreOCC(ci.next(), list);      
}
private void PreO(Tree rt, LinkedList<Tree> list, int count)
{
    list.add(rt);
    rt.setX_vcoordinate(count);
    Iterator<Tree> ci = rt.ReverseChildrenIterator();
    while(ci.hasNext())
        PreO(ci.next(), list, ++count);
}

这里我生成树的结构:

Tree root = new Tree(new Integer(0));
root.addChild(new Tree(new Integer(1), root));
root.addChild(new Tree(new Integer(2), root));
root.addChild(new Tree(new Integer(3), root));
Iterator<Tree> ci = root.ChildrenIterator();
ci.next();
Tree select = ci.next();
select.addChild(new Tree(new Integer(4), select));
select.addChild(new Tree(new Integer(5), select));

这是我打印节点遍历的顺序以及它分配给各个节点的坐标时的输出。

0 3 2 5 4 1
0 1 2 3 4 3

0 1 2 4 5 3
0 1 2 3 4 3

注意:前两行是顺时针前序遍历和 x 坐标的分配。接下来的两行是逆时针前序遍历并分配它们的 y 坐标。

我的问题是如何阅读第二行: 0 1 2 3 4 5

编辑 1:这是我用来打印访问节点的顺序和分配的坐标的代码。

Iterator<Tree> pre = root.PreOrder(true);
System.out.println("              \t");
while(pre.hasNext())
    System.out.print(pre.next() + "\t");
    
pre = root.PreOrder(true);
System.out.println();
System.out.println("x-coordinates:\t");
while(pre.hasNext())
System.out.print(pre.next().getVirtual_x() + "\t");
    
System.out.println();
System.out.println();
    
Iterator<Tree> preCC = root.PreOrder(false);
System.out.println("              \t");
while(preCC.hasNext())
    System.out.print(preCC.next() + "\t");
    
preCC = root.PreOrder(false);
System.out.println();
System.out.println("x-coordinates:\t");
while(preCC.hasNext())
System.out.print(preCC.next().getVirtual_y() + "\t");

这里还引用了一个更好地解释 x,y 坐标的引用。 顶点。顶点的 y 坐标。

逆时针计算 T 的顶点的预排序( 排序从 0 到 n 编号 - 1),将它们用作 x 坐标 顶点。

计算顺时针预排序 T 的顶点(排序为 编号从 0 到 n - 1),将它们用作 顶点的 y 坐标。

【问题讨论】:

  • 我同意。这使得更难理解在这里做什么!
  • Nathan 你确定你正在构建的树有你想要的结构吗?我看不到节点的预先遍历将如何按照 012345 的顺序访问它们。
  • @matt:我没有按这个顺序访问他们。访问节点的顺序是最上面一行。我将发布所有这些代码,因为它似乎让很多人感到困惑。

标签: java pass-by-reference graph-theory pass-by-value


【解决方案1】:

Java 的值传递,总是——对于基元和对象。它是为非原始对象传递的引用,因此您可以更改它们指向的对象的状态,但不能更改引用本身。

来自“Java 编程语言”中的 James Gosling:

"...只有一个参数 Java中的传递模式-按值传递- 这让事情变得简单。 ..”

我认为这是对此的最终权威。

我意识到这对于 Java 程序员来说是一个激烈争论、有争议的话题

不,没有辩论。詹姆斯·高斯林从一开始就将这一点融入了语言中。如果您认为这是有争议的,那您可悲的是被误导或无知。

【讨论】:

  • 好的,我明白这一点,但这如何帮助我解决我的问题?问这个问题的原因不是为了引发辩论,而是为了解决我提出的问题。
  • 可悲的是,那里有 辩论——当然,一方面是不知情的。昨天晚上,一些随机的小伙子给我发了一封电子邮件,试图让我相信 Java 总是使用引用传递:(
  • 对我来说,这就像创造论与进化论:双方的权重不相等。
  • @Nathan - 抱歉,您提出问题的方式让您听起来不仅认为 Java 是通过引用传递的,而且它是您问题的解决方案。如果你不相信,我会编辑你的措辞。我也会让你的问题更清楚,因为递归的树遍历通常在 Java 中完成。也许您还缺少其他东西。
  • 我认为他们差不多。
【解决方案2】:

其实在Java中有一种通过引用传递的方式:

class Pointer {
    private Object ptr;
    public Pointer(Object v)  { set(v);  }
    public void set(Object v) { ptr = v; }
    public Object get()       { return ptr; }
}

然后你使用它们:

public void swap(Pointer a, Pointer b) {
    Object tmp = a.get();
    a.set(b.get());
    b.set(tmp);
}

并像这样调用交换:

public static void main(String[] args) {
    Integer one = 1; 
    Integer two = 2;
    Pointer pone; pone.set(one);
    Pointer ptwo; ptwo.set(two);
    swap(pone, ptwo);
    System.out.println((Integer) pone.get());
    System.out.println((Integer) ptwo.get());
}

不过,如果你真的这样做了,那么你可能是疯了,或者只是还没有考虑使用 Java。

【讨论】:

  • 明确一点,这并不是实际上通过引用传递的。它通过额外的间接级别模拟按引用传递。我并不是说它不起作用,只是它不像让语言改变它的传递方案:)
  • 这不是通过引用传递 - 抱歉。
  • @duffymo:是的,它不是通过引用传递,它更类似于通过指针传递。如果将所有a.get() 替换为*a 并将a.set(...) 替换为*a = ...,您将从C/C++ 中获得更熟悉的传递指针。指针通常被认为是一种特定的引用形式,因此按指针传递是按引用传递的一种特定类型(尽管有些纯粹主义者可能不同意,因为指针本身仍然是按值传递的)。
  • @duffymo:另外,你需要一点幽默感;)
  • 当我看到幽默的东西时,我会笑。如果你用这段代码 Lie Ryan 来表达你的讽刺,恐怕 SO、互联网和浏览器都会过滤掉它。
【解决方案3】:

你不需要通过引用传递,你需要一个有副作用的函数。通过引用传递将是实现此目的的一种方法;使用可变对象作为函数参数将是另一种情况。

private void PreO(Tree rt, LinkedList<Tree> list, int[] count)
{
    list.add(rt);
    rt.setX_vcoordinate(count[0]);
    Iterator<Tree> ci = rt.ReverseChildrenIterator();
    while(ci.hasNext()) {
        ++count[0];
        PreO(ci.next(), list, count);
    }
}

【讨论】:

    猜你喜欢
    • 2011-12-12
    • 1970-01-01
    • 2012-10-10
    • 1970-01-01
    • 1970-01-01
    • 2020-10-02
    • 1970-01-01
    • 2012-03-13
    相关资源
    最近更新 更多