假设以下功能:
int binaryTree::findHeight(node *n) {
if (n == NULL) {
return 0;
} else {
return 1 + max(findHeight(n->left), findHeight(n->right));
}
}
给定二叉搜索树treeHeight
的标准递归binaryTree
函数。现在,我正在帮助一个朋友(他正在参加算法课程),我遇到了一个奇怪的问题,我无法100%向他解释这个功能。
将max定义为max(a,b) ((a)>(b)?(a):(b))
(恰好是windef.h
中的最大定义),递归函数变得怪异(它运行n^n
次n
是树高)。这显然使得用3000个元素检查树的高度需要非常长的时间。
但是,如果通过模板定义max,就像std
那样,一切都没问题。因此使用std::max
解决了他的问题。我只想知道原因。
另外,为什么countLeaves
函数使用相同的程序递归工作正常?
int binaryTree::countLeaves(node *n) {
if (n == NULL) {
return 0;
} else if (n->left == NULL && n->right == NULL) {
return 1;
} else {
return countLeaves(n->left) + countLeaves(n->right);
}
}
是否因为在返回三元函数时,值a => countLeaves(n->left)
和b => countLeaves(n->right)
被递归地双重调用只是因为它们是结果?
谢谢!
我只想链接一些有关该主题的文献以供将来参考:
http://www.boostpro.com/tmpbook/preprocessor.html
http://msdn.microsoft.com/en-us/library/z3f89ch8.aspx
两种实现的主要区别在于:
#define max(i, j) (((i) > (j)) ? (i) : (j))
VS
template<class T> T max (T i, T j) { return ((i > j) ? i : j) }
谢谢大家!
答案 0 :(得分:11)
在编译器查看代码之前,预处理器会扩展宏。这意味着,例如,宏参数可能会被多次评估。
使用你的宏,你最终会得到类似于:
的东西int binaryTree::findHeight(node *n) {
if (n == NULL) {
return 0;
} else {
return 1 + (findHeight(n->left) > findHeight(n->right)) ? // call once...
findHeight(n->left) : findHeight(n->right); // and ouch
}
}
正如您所看到的,它将评估这两个功能,然后再花费一个额外的时间。这就是为什么宏可能是邪恶的。
您可以在包含Windows标头之前定义NOMINMAX
来禁用宏。然后使用<algorithm>
中的函数。
如果他必须使用宏,他必须将计算存储在一个变量中:
int binaryTree::findHeight(node *n) {
if (n == NULL) {
return 0;
} else {
const int leftHeight = findHeight(n->left);
const int rightHeight = findHeight(n->right);
return 1 + max(leftHeight, rightHeight);
}
}
使用一个函数,每个调用将在之前进行评估,以调用该函数。也就是说,它有点像以前的代码块。它评估函数的参数,获取结果,然后将它们传递给std::max
函数。没有重复的评估。
答案 1 :(得分:2)
max max对参数进行两次计算 - 因为你的参数是一个递归函数调用,这可能是perf问题的根源。
答案 2 :(得分:0)
这是因为max的定义。你正在调用findHeight()而不是2。
答案 3 :(得分:0)
更好的选择是声明具有以下签名的函数:
int max(int, int)
这将阻止宏的递归扩展。