【问题标题】:Returning recursive ternary freaks out返回递归三元怪胎
【发布时间】:2011-01-27 13:55:29
【问题描述】:

假设以下函数:

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 中的 max 定义)时,递归函数会出现问题(它运行类似于 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) 的值被递归地调用 double 只是因为它们是结果?

谢谢!

问题在下面得到回答

我只是想链接一些有关该主题的文献以供将来参考:
http://www.boostpro.com/tmpbook/preprocessor.html
http://msdn.microsoft.com/en-us/library/z3f89ch8.aspx

这两种实现的主要区别是:

#define max(i, j) (((i) > (j)) ? (i) : (j))

template<class T> T max (T i, T j) { return ((i > j) ? i : j) }

谢谢大家!

【问题讨论】:

  • 用它的定义替换 max 宏,你就会明白为什么了。
  • 这两个功能都可以正常工作。第一个失败的唯一原因是它使用了max 宏。第二个没有。
  • 宏是邪恶的。避免使用它们,直到您确定没有其他方法(通过经验知道什么时候会更容易)。

标签: c++ recursion binary-tree


【解决方案1】:

在编译器看到代码之前,预处理器会扩展宏。这意味着,例如,宏参数可能会被多次评估。

使用你的宏,你最终会得到类似于:

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 来禁用宏。然后改用&lt;algorithm&gt;中的函数。

如果他必须使用宏,他必须将计算结果存储在一个变量中:

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 函数。没有重复评价。

【讨论】:

  • 该死的。当我家停电时,我把这一切都写下来了,我失去了一切。 FUUUU-
  • 谢谢,我已经知道宏是问题所在:P 但我只是想知道为什么 std 实现有效,而 windef.h 无效(具体来说,它们在编程上是如何不同的)。
  • @David:我已经编辑澄清,如果这回答了你的问题,请告诉我。
  • 你确实做到了(我接受了)。但是,我已经知道宏和函数之间的区别(我不是新手程序员;p),但 STL 使用的模板我也认为是由预处理器扩展的(即模板元编程的某些情况)。跨度>
  • 模板只是编写代码。模板函数是函数的模板,编译器插入类型并获取这些类型的函数。没什么特别的,真的。预处理器用于文本,模板用于代码。预处理器会扩展什么?它无法为某些未来类型生成函数。
【解决方案2】:

该 max 宏对参数进行两次评估 - 由于您的参数是递归函数调用,这可能是性能问题的根源。

【讨论】:

    【解决方案3】:

    这是因为 max 的定义。您正在调用 3 次 findHeight() 而不是 2 次。

    【讨论】:

      【解决方案4】:

      更好的选择是声明一个具有以下签名的函数:

      int max(int, int)
      

      这将防止宏的递归扩展。

      【讨论】:

      • 不用了,那个函数已经以std::max的形式存在了。
      猜你喜欢
      • 1970-01-01
      • 2020-03-14
      • 2016-01-25
      • 1970-01-01
      • 2022-01-03
      • 2022-11-23
      • 1970-01-01
      • 2016-01-24
      • 2018-10-25
      相关资源
      最近更新 更多