C 递归函数:用于vs if

C 递归函数:用于vs if,c,binary-search-tree,C,Binary Search Tree,在其中一个C练习中,我必须为给定深度的二叉树遍历创建一个函数 我的第一个想法是使用for循环(transverse\u preorder\u bad)。最后,如果(traverse\u preorder\u working),我可以通过变量初始化+完成任务,但我仍在努力理解为什么的解决方案不起作用 有人能给我解释一下区别吗?有优雅的解决方案吗 #包括 #包括 #包括 常数int范围=1000; 类型定义结构节点 { int数据; 结构节点_t*左; 结构节点(右); }节点t; node_t*

在其中一个C练习中,我必须为给定深度的二叉树遍历创建一个函数

我的第一个想法是使用for循环(
transverse\u preorder\u bad
)。最后,如果(
traverse\u preorder\u working
),我可以通过变量初始化+
完成任务,但我仍在努力理解为什么
解决方案不起作用

有人能给我解释一下区别吗?有优雅的解决方案吗

#包括
#包括
#包括
常数int范围=1000;
类型定义结构节点
{
int数据;
结构节点_t*左;
结构节点(右);
}节点t;
node_t*node_new(int数据);
node_t*node_insert(node_t*树,int数据);
无效遍历(节点树,整数深度);
无效遍历\u预序\u坏(节点\u t*树,int深度);
int main(int argc,char*argv[])
{
node_t*tree=NULL;
//随机种子
srand((无符号)时间(NULL));
//创建根节点
树=节点\新(rand()%范围);
节点插入(树,5);
节点插入(树,1);
printf(“预期输出:\n”);
遍历预排序工作(树,10);
printf(“错误输出:\n”);
遍历前序(树,10);
返回0;
}
node_t*node_new(整数数据)
{
节点树;
tree=malloc(sizeof(*tree));
树->左=空;
树->右=空;
树->数据=数据;
回归树;
}
node_t*node_insert(node_t*树,int数据)
{
如果(!树)
返回新节点(数据);
如果(数据==树->数据)
回归树;
如果(数据<树->数据)
树->左=节点插入(树->左,数据);
其他的
树->右=节点\插入(树->右,数据);
回归树;
}
无效遍历\u预排序\u工作(节点\u t*树,整数深度)
{
int i;
如果(!树)
返回;
printf(“%d\n”,树->数据);
i=1;
if(tree->left&&i left,depth-i);
i++;
}
i=1;
if(树->右&i右,深度-i);
i++;
}
}
无效遍历\u预序\u错误(节点\u t*树,整数深度)
{
如果(!树)
返回;
printf(“%d\n”,树->数据);
对于(inti=1;tree->left&&i left,depth-i);
对于(inti=1;树->右&i右,深度-i);
}

问题在于,当访问您调用的节点时,
遍历预排序工作
在左子树(然后是右子树)上递归地进行时,
遍历预排序工作
是正确的递归

相反,
traverse\u preorder\u bad
仍然是递归的,但它没有任何意义,当您访问一个节点时,然后在同一子树上以不同深度调用
traverse\u preorder\u bad
n次

如果您检查调用树中的以下内容:

       a
      / \
     b   c
    / \ / \
    d e f g
您可以看到
travel\u preorder\u working(a,5)
转到
travel\u preorder\u working(b,4)
travel\u preorder\u working(d,3)
。。当其他功能消失时

traverse_preorder_bad(a,5),
traverse_preorder_bad(b,4), visit subtree
traverse_preorder_bad(b,3), visit subtree
traverse_preorder_bad(b,2), visit subtree
traverse_preorder_bad(b,1), visit subtree ...
从相同的递归级别,这意味着每个节点将以不同的深度限制访问多次;这不会发生在第一个正确的版本中

如果每次调用
traverse\u preorder\u bad
都应该访问一个节点并开始访问两个子树,但是在代码内部,您递归调用的访问次数超过两次(这种情况下,因为您有一个循环),那么就有问题了

这里的优雅解决方案(没有递归)是莫里斯·特拉弗斯。其思想是将左子树最右侧节点的间接边添加到当前节点

算法的完整解释如下:

当然,您可以修改此算法,使其不会比当前深度更深。

对于“for”版本没有任何意义。您只想打印一次给定节点的树,因此应该只对每个节点调用一次遍历

此外,根据你在帖子中的一条评论,我认为你对自己的工作职能有一些误解

您有多个检查树是否为空(作为当前树或其子树)

i
在使用时仅具有一个值。你可以简化为

void traverse_preorder_working(node_t *tree, int depth){
    if(!tree || depth <= 0){
        return;
    }
    printf("%d\n", tree->data);
    traverse_preorder_working(tree->left, depth - 1);
    traverse_preorder_working(tree->right, depth - 1);
}
无效遍历\u预排序\u工作(节点\u t*树,整数深度){
if(!tree | | depth data);
遍历预排序工作(树->左,深度-1);
遍历(树->右,深度-1);
}

所有的检查,看看我们是否应该探索一个节点-无论是因为它不存在或太深-只做了一次(在函数的开始),而不是重复两次为每个孩子。没有
i变量不起任何作用。

我认为这个算法完全错误,OP没有试图比纠正更聪明。不,你可以亲自评估这个算法。这也是众所周知的算法,只有谷歌。你说的优雅是什么意思?递归是绝对优雅的。递归的主要问题是调用激活帧的内存/时间要求,但除此之外,递归非常优雅。@Jack,递归很棒!我只是不喜欢我的解决方案。必须两次设置一个helper变量,一件简单的事情需要很多行…现在,这很优雅。非常感谢。
void traverse_preorder_working(node_t *tree, int depth){
    if(!tree || depth <= 0){
        return;
    }
    printf("%d\n", tree->data);
    traverse_preorder_working(tree->left, depth - 1);
    traverse_preorder_working(tree->right, depth - 1);
}