C ++中的可变与不可变二进制树遍历

时间:2016-03-09 05:03:21

标签: c++ binary-tree immutability mutability

我来自Python和Haskell背景,所以当我必须编写一个函数来计算二叉树中的电影数量时,我这样做了:

int MovieTree::countMovieNodes(MovieNode* parent)
{
  if (parent)
    return countMovieNodes(parent->left) + countMovieNodes(parent->right) + 1;
  else
    return 0;
}

int MovieTree::countMovieNodes()
{ countMovieNodes(root); }

但是,使用课堂上提供的头文件,我必须这样做:

void MovieTree::countMovieNodes(MovieNode* parent, int* c)
{
  (*c)++;
  if (parent->left)
    countMovieNodes(parent->left, c);
  if (parent->right)
    countMovieNodes(parent->right, c);
}

int MovieTree::countMovieNodes()
{
  if (!root) return 0;
  int c=0; countMovieNodes(root, &c); return c;
}

我很了解前者对后者的好处;后者比前者有什么好处?它与堆栈内存使用有关,对吗?

2 个答案:

答案 0 :(得分:1)

在第二种解决方案中,我认为第一种解决方案没有任何优势。事实上,我认为第一个更糟糕。第一个是更干净的,用更少的代码行来检查空指针。

但是,您的问题标题具有误导性。第二个也是递归的。它不是迭代的。

答案 1 :(得分:1)

后一种方式在C ++风格中做得不好的一件事是,它仍然为了通过引用调用而传递指针,为此在C ++中我们有引用使代码更清晰且更不容易出错:

void MovieTree::countMovieNodes(MovieNode* parent, int& count)
{
  ++count;
  ....
}

回到递归的风格,后一种情况使用的风格,由于尾调用优化,可以有更好的性能。

简而言之,TCO避免了每个递归调用级别的调用堆栈不断增加。

在前一种形式中,我们需要为每个级别的递归调用保持堆栈,以便我们可以得到结果并进行进一步的计算(在您的情况下,计算两个子树中的计数总和)。

对于后一种情况,编译器知道它不需要为第二次调用countMovieNodes保留堆栈(因为没有进一步的计算),以便它可以重用当前堆栈来执行递归调用(如果我的理解正确,则仅用于第二次调用)。

但是,在这种特定情况下,利益可能不是那么大,因为对于后一种情况,countMovieNodes的第一次呼叫不能从TCO中受益。它仍然是一个优势。