在Java中计算树中的节点

时间:2009-02-13 20:50:39

标签: java recursion tree

首先,我发誓这不是家庭作业,这是我在接受采访时被问到的一个问题。我想我弄得一团糟(尽管我确实意识到解决方案需要递归)。这是一个问题:

实现count()方法,该方法返回树中的节点数。如果某个节点没有左或右子节点,相关的getXXChild()方法将返回null

class Tree {

  Tree getRightChild() {
    // Assume this is already implemented
  }

  Tree getLeftChild() {
    // Assume this is already implemented
  }

  int count() {
    // Implement me
  }
}

我提出问题的理由只是好奇地看到了正确的解决方案,从而衡量了我的糟糕程度。

干杯, 贝

15 个答案:

答案 0 :(得分:32)

int count() {
  Tree right = getRightChild();
  Tree left = getLeftChild();
  int c = 1;                                      // count yourself!
  if ( right != null ) c += right.count();        // count sub trees
  if ( left != null ) c += left.count();          // ..
  return c;
}

答案 1 :(得分:19)

一个简单的递归解决方案:

int count() {
   Tree l = getLeftTree();
   Tree r = getRightTree();
   return 1 + (l != null ? l.count() : 0) + (r != null ? r.count() : 0);
}

一个不那么简单的非递归的:

int count() {
    Stack<Tree> s = new Stack<Tree>();
    s.push(this);
    int cnt = 0;
    while (!s.empty()) {
        Tree t = s.pop();
        cnt++;
        Tree ch = getLeftTree();
        if (ch != null) s.push(ch); 
        ch = getRightTree();
        if (ch != null) s.push(ch); 
    }
    return cnt;
}

后者可能稍微提高内存效率,因为它用堆栈和迭代替换了递归。它也可能更快,但没有测量就很难分辨。一个关键的区别是递归解决方案使用堆栈,而非递归解决方案使用堆来存储节点。

编辑:以下是迭代解决方案的一种变体,它使用堆栈的次数较少:

int count() {
    Tree t = this;
    Stack<Tree> s = new Stack<Tree>();
    int cnt = 0;
    do {
        cnt++;
        Tree l = t.getLeftTree();
        Tree r = t.getRightTree();
        if (l != null) {
            t = l;
            if (r != null) s.push(r);
        } else if (r != null) {
            t = r;
        } else {
            t = s.empty() ? null : s.pop();
        }
    } while (t != null);
    return cnt;
}

您是否需要更高效或更优雅的解决方案,自然取决于树木的大小以及您打算使用此例程的频率。 Rembemer Hoare说:“过早优化是万恶之源。”

答案 2 :(得分:11)

我更喜欢这个,因为它写着:

左侧的返回计数+ rigth的计数+ 1

  int count() {
      return  countFor( getLeftChild() ) + countFor( getRightChild() ) + 1;
  }
  private int countFor( Tree tree )  { 
       return tree == null ? 0 : tree.count();
  }

更多的文字编程。

顺便说一句,我不喜欢Java上常用的getter / setter约定,我认为使用 leftChild()会更好:

  return countFor( leftChild() ) + countFor( rightChild() ) + 1;

就像Hoshua Bloch在这里解释http://www.youtube.com/watch?v=aAb7hSCtvGw一样。 32:03

  

如果你得到它,你的代码就是......

但是,我必须承认get / set约定现在几乎是语言的一部分。 :)

对于许多其他部分,遵循此策略会创建自我记录代码,这是一件好事。

托尼:我想知道,你在采访中的回答是什么。

答案 3 :(得分:4)

这样的事情应该有效:

int count()
{
    int left = getLeftChild() == null ? 0 : getLeftChild().count();
    int right = getRightChild() == null ? 0 : getRightCHild().count();

    return left + right + 1;
}

答案 4 :(得分:4)

return (getRightChild() == null ? 0 : getRightChild.count()) + (getLeftChild() == null ? 0 : getLeftChild.count()) + 1;

或类似的东西。

答案 5 :(得分:4)

class Tree {

  Tree getRightChild() {
    // Assume this is already implemented
  }

  Tree getLeftChild() {
    // Assume this is already implemented
  }

  int count() {
   return 1 
      + getRightChild() == null? 0 : getRightChild().count()
      + getLeftChild() == null? 0 : getLeftChild().count();
  }
}

答案 6 :(得分:2)

您可以通过遍历多个ways来计算树。只需预先遍历遍历,代码就是(基于您定义的函数):

int count() {
    count = 1;
    if (this.getLeftChild() != null)
        count += this.getLeftChild().count();
    if (this.getRightChild() != null)
        count += this.getRightChild().count();
    return count;
}

答案 7 :(得分:2)

实施方法:

public static int countOneChild(Node root)
{
    ...
}

计算具有一个子节点的二叉树中的内部节点数。将该功能添加到tree.java程序。

答案 8 :(得分:1)

我是通过预购递归来做到的。尽管使用localRoot并没有完全遵循访谈格式,但我认为你明白这一点。

get_data

答案 9 :(得分:0)

这是一个标准的递归问题:

count():
    cnt = 1 // this node
    if (haveRight) cnt += right.count
    if (haveLeft)  cnt += left.count
return cnt;

非常低效,如果树很深,那就是杀手,但这是你的递归......

答案 10 :(得分:0)

int count()

{
   int retval = 1;
    if(null != getRightChild()) retval+=getRightChild().count();
    if(null != getLeftChild()) retval+=getLeftChild().count();
    return retval;

}

上帝,我希望我没有犯错误。

编辑:我确实做到了。

答案 11 :(得分:0)

当然,如果您想在计算时避免访问树中的每个节点,并且处理时间比内存更有价值,那么您可以通过在构建树时创建计数来作弊。

  1. 每个节点都有一个int计数, 初始化为一个,哪个 重新表示节点的数量 以该节点为根的子树。

  2. 之前插入节点时 从递归插入返回 例程,增加计数 当前节点。

  3. 即。

    public void insert(Node root, Node newNode) {
      if (newNode.compareTo(root) > 1) {
        if (root.right != null) 
          insert(root.right, newNode);
        else
          root.right = newNode;
      } else {
        if (root.left != null)
          insert(root.left, newNode);
        else
          root.left = newNode;
      }
      root.count++;
    }
    

    然后从任何一点获取计数只需要查找node.count

答案 12 :(得分:0)

我的第一次尝试没有添加任何新内容,但后来我开始怀疑递归深度以及是否可以重新排列代码以利用最新Java编译器的尾调用优化功能。主要问题是null测试 - 可以使用NullObject解决。我不确定TCO是否可以处理递归调用,但它至少应该优化最后一个。

static class NullNode extends Tree {

    private static final Tree s_instance = new NullNode();

    static Tree instance() {
        return s_instance;
    }

    @Override
    Tree getRightChild() {  
        return null;
    }  

    @Override
    Tree getLeftChild() {  
        return null;
    }  

    int count() {  
        return 0;
    }
}

int count() {      
    Tree right = getRightChild();      
    Tree left  = getLeftChild();      

    if ( right == null ) { right = NullNode.instance(); }
    if ( left  == null ) { left  = NullNode.instance(); }

    return 1 + right.count() + left.count();
}   

NullNode的精确实现取决于Tree中使用的实现 - 如果Tree使用NullNode而不是null,那么子访问方法可能会抛出NullPointerException而不是返回null。无论如何,主要的想法是使用NullObject以试图从TCO获益。

答案 13 :(得分:0)

在访谈中应该预期与二叉树相关的问题。我会说在下次采访之前需要时间并通过this链接。解决了大约14个问题。您可以查看解决方案是如何完成的。这将让您了解如何在将来解决二叉树问题。

我知道您的问题特定于计数方法。这也在我提供的链接中实现

答案 14 :(得分:0)

B