首先,我发誓这不是家庭作业,这是我在接受采访时被问到的一个问题。我想我弄得一团糟(尽管我确实意识到解决方案需要递归)。这是一个问题:
实现count()方法,该方法返回树中的节点数。如果某个节点没有左或右子节点,相关的getXXChild()
方法将返回null
class Tree {
Tree getRightChild() {
// Assume this is already implemented
}
Tree getLeftChild() {
// Assume this is already implemented
}
int count() {
// Implement me
}
}
我提出问题的理由只是好奇地看到了正确的解决方案,从而衡量了我的糟糕程度。
干杯, 贝
答案 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)
当然,如果您想在计算时避免访问树中的每个节点,并且处理时间比内存更有价值,那么您可以通过在构建树时创建计数来作弊。
每个节点都有一个int计数, 初始化为一个,哪个 重新表示节点的数量 以该节点为根的子树。
之前插入节点时 从递归插入返回 例程,增加计数 当前节点。
即。
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