我面对经典的“它有效,但我不知道为什么!” - 问题。我只是应用了一个我从另一个有整数的练习中知道的原则,但在这里我必须使用树木。该方法的测试是成功的。我应该计算一棵树的结,我这样做遍历它(在这种情况下:inorder),每次我成功遍历(意思是:没有面对一个空的子树),我认为这是一个结。在这种情况下,我想知道为什么这个代码不算太多结。例如,当我总是向左走,面对一个空的子树时,我不会上去,直到我到达一个可以向右走的结?为什么我的代码会避免这种问题?
public static int numberKnots (Tree b) {
int count = 0;
if (b.empty()) {
return 0;
}
else {
traverse.inorder(b.left());
traverse.inorder(b.right());
count = 1;
}
return count + numberKnots(b.left()) + numberKnots(b.right());
}
答案 0 :(得分:1)
你并没有真正在树上走来走去,你只能走下去,每次访问每个节点,然后通过让你的树变得越来越简单来做到这一点。
考虑以下树
a
/ \
b c
/ \
d e
所以你从根开始并检查它是否为空它不是,所以你返回1 + numberKnots(左)+ numberKnots(右)的结果。左边和右边也是树木,它们比
更简单left right
b c
/ \
d e
所以现在你检查b树,它是空的,所以它只返回0.然后你检查c树,它不是空的,所以你返回1 + countKnots(左(c))+ countKnots(右(的) c))等等。
计算的每一步都是:
countKnots(a)
= 1 + countKnots(b) + countKnots(c)
= 1 + 0 + countKnots(c)
= 1 + 0 + 1 + countKnots(d) + countKnots(e)
= 1 + 0 + 1 + 0 + countKnots(e)
= 1 + 0 + 1 + 0 + 0
= 2
您的代码可以简化为
public static int numberKnots (Tree b) {
if (b.empty()) {
return 0;
} else {
return 1 + numberKnots(b.left()) + numberKnots(b.right());
}
}
但是,它似乎不处理不包含左节点和右节点的树节点,因此以下树将导致错误
a
\
c
答案 1 :(得分:0)
由于你正在使用递归,它不会两次横向相同的节点。所以你的代码工作得很好。考虑(A)有两个子节点(B)&(C)进一步(B)有两个子节点(B1) )&(B2)。当横穿递归时,它会使用堆栈。“(将s视为我们的堆栈)”。首先控制到达节点(A)并且因为它已经将子(A)推入堆栈并且控制被转移到(B)现在控制被转移到(B)的左子(B1)并且(B)被推送到堆栈,因为(B1)没有任何子项,它被计数并且控制被转移到堆栈顶部即(B)和(B)现在计算控制被转移到(B)的右子(B2)和(B2)被计算并且控制被转移到(B)。现在(B)没有剩下任何代码的部分,因此它从堆栈弹出并且控制被转移到(A)和(A)被计算并且控制被转移到了正确的孩子(c)。同样地,所有节点都被计算在内而没有重复。
希望有所帮助