递归函数:对于vs if

时间:2016-01-08 00:40:53

标签: c binary-search-tree

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

我的第一个想法是使用for循环(traverse_preorder_bad)。最后,我可以使用变量初始化+ iftraverse_preorder_working)完成任务,但我仍然在努力理解为什么for解决方案不起作用。

有人可以解释我的区别吗?有优雅的解决方案吗?

Code on Ideone

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

const int RANGE = 1000;

typedef struct node_t
{
    int data;
    struct node_t *left;
    struct node_t *right;
} node_t;

node_t *node_new(int data);
node_t *node_insert(node_t *tree, int data);
void traverse_preorder_working(node_t *tree, int depth);
void traverse_preorder_bad(node_t *tree, int depth);

int main(int argc, char *argv[])
{
    node_t *tree = NULL;

    // Random seed
    srand((unsigned) time(NULL));

    // Create the root node
    tree = node_new(rand() % RANGE);

    node_insert(tree, 5);
    node_insert(tree, 1);

    printf("Expected output:\n");
    traverse_preorder_working(tree, 10);

    printf("Bad output:\n");
    traverse_preorder_bad(tree, 10);

    return 0;
}

node_t *node_new(int data)
{
    node_t *tree;

    tree = malloc(sizeof(*tree));
    tree->left = NULL;
    tree->right = NULL;
    tree->data = data;

    return tree;
}

node_t *node_insert(node_t *tree, int data)
{
    if (!tree)
        return node_new(data);

    if (data == tree->data)
        return tree;
    if (data < tree->data)
        tree->left = node_insert(tree->left, data);
    else
        tree->right = node_insert(tree->right, data);

    return tree;
}

void traverse_preorder_working(node_t *tree, int depth)
{
    int i;

    if (!tree)
        return;

    printf("%d\n", tree->data);

    i = 1;
    if (tree->left && i <= depth)
    {
        traverse_preorder_working(tree->left, depth - i);
        i++;
    }

    i = 1;
    if (tree->right && i <= depth)
    {
        traverse_preorder_working(tree->right, depth - i);
        i++;
    }
}

void traverse_preorder_bad(node_t *tree, int depth)
{
    if (!tree)
        return;

    printf("%d\n", tree->data);

    for (int i = 1; tree->left && i <= depth; i++)
        traverse_preorder_bad(tree->left, depth - i);

    for (int i = 1; tree->right && i <= depth; i++)
        traverse_preorder_bad(tree->right, depth - i);
}

3 个答案:

答案 0 :(得分:4)

当访问在左子树上递归调用traverse_preorder_working的节点(然后是右)时,问题是traverse_preorder_working是正确递归的

相反,traverse_preorder_bad仍然是递归的,但是没有意义,当您访问某个节点时,您会在同一个具有不同深度的子树上调用traverse_preorder_bad n次。

如果您检查调用树,请执行以下操作:

       a
      / \
     b   c
    / \ / \
    d e f g

您可以看到traverse_preorder_working(a,5)traverse_preorder_working(b,4)traverse_preorder_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_preorder_bad的每个调用都应该访问一个节点并开始访问两个子树,但在代码内部,您调用的访问次数超过两次(就是这样,因为你有一个循环)然后出现问题

答案 1 :(得分:2)

“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);
}

所有检查以查看我们是否应该探索节点 - 无论是因为它不存在还是太深 - 都只进行一次(在函数开始时),并且不会为每个孩子重复两次。没有任何内容的i变量。

答案 2 :(得分:0)

这里优雅的解决方案(没有递归)是Morris Traversal。我们的想法是从左子树的最右边节点向当前节点添加间接边缘。

该算法的完整说明如下:http://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion-and-without-stack/

当然,你可以修改这个算法,不要比你目前的深度更深。