【问题标题】:Print tree node and all of it's childs efficiently有效打印树节点及其所有子节点
【发布时间】:2019-04-12 17:18:39
【问题描述】:

我正在尝试创建一个可以打印节点及其所有子节点的函数,但我正在尝试使其高效且递归。但它并没有真正起作用。

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

#define SIZE    100

typedef struct tree {
    int value;
    struct tree *child, *sibling, *parent;
} *Tree;

Tree initTree(int value) {
    Tree root = malloc(sizeof(struct tree));
    root->value = value;
    root->parent = NULL;
    root->child = NULL;
    root->sibling = NULL;
    return root;
}

void drawTreeHelper(Tree tree, FILE* stream) {
    Tree tmp;
    if (tree == NULL) {
        return;
    }
    fprintf(stream, "    %ld[label=\"%d\", fillcolor=red]\n", (intptr_t) tree, tree->value);
    tmp = tree->child;

    while (tmp != NULL) {
        fprintf(stream, "    %ld -> %ld \n", (intptr_t) tree, (intptr_t) tmp);
        drawTreeHelper(tmp, stream);
        tmp = tmp->sibling;
    }
}

void drawTree(Tree tree, char *fileName) {
    FILE* stream = fopen("test.dot", "w");
    char buffer[SIZE];
    fprintf(stream, "digraph tree {\n");
    fprintf(stream, "    node [fontname=\"Arial\", shape=circle, style=filled, fillcolor=yellow];\n");
    if (tree == NULL)
        fprintf(stream, "\n");
    else if (!tree->child)
        fprintf(stream, "    %ld [label=\"%d\"];\n", (intptr_t) tree, tree->value);
    else
        drawTreeHelper(tree, stream);
    fprintf(stream, "}\n");
    fclose(stream);
    sprintf(buffer, "dot test.dot | neato -n -Tpng -o %s", fileName);
    system(buffer);
}

Tree uniteTries(Tree child, Tree parent)
{
    if (parent)
    {
        if (!parent->child) parent->child = child;
        else
        {
            Tree iter = parent->child;
            while (iter->sibling) iter = iter->sibling;
            iter->sibling = child;
        }
    }
    return parent;
}

Tree uniteForest(Tree root, Tree *forest, int n)
{
    int i;
    for (i = 0; i < n; ++i)
    {
        if (forest[i]) root = uniteTries(forest[i], forest[i]->parent);
    }
    root = forest[0];
    return root;
}

void printParentChildRec(Tree root)
{
    if(!root) return;
    printf("%d ", root->value);

    printParentChildRec(root->sibling);
    printParentChildRec(root->child);
}

int main() {
    int i;
    char buffer[SIZE];
    Tree *forest = malloc(6 * sizeof(Tree));
    for (i = 0; i < 6; i++) {
        forest[i] = initTree(i);
    }

    forest[1]->parent = forest[0];
    forest[2]->parent = forest[0];
    forest[3]->parent = forest[0];
    forest[4]->parent = forest[1];
    forest[5]->parent = forest[1];

    Tree root = uniteForest(root, forest, 6);

    printParentChildRec(root);

    drawTree(root, "tree.png");

    return 0;
}

此代码将为您提供一个可验证的示例,这是我尝试做的:

void printParentChildRec(Tree root) {
    if (!root)
        return;
    printf("%d ", root->value);

    printParentChildRec(root->sibling);
    printParentChildRec(root->child);
}

我得到的结果只是 0 1 2 3 4 5 这是所有节点,但我想打印这样的东西:

0 1 2 3
1 4 5
2
3
4
5

【问题讨论】:

    标签: c tree tree-traversal multiway-tree


    【解决方案1】:

    你的代码有一些问题:

    • 您将指针隐藏在 typedef 后面,这会让您的代码阅读者感到困惑,并经常导致编程错误。
    • 您使用%ld 打印intptr_t 值,这是在intptr_t 不是long 的别名并且具有不同大小的平台上的未定义行为,例如Windows 64 位。此类型没有特定的printf 格式,您应该将值重新转换为(long)(intptr_t)tree(long long)(intptr_t)tree 并使用%lld,或其未签名版本或使用%p 格式和(void *)tree。李>
    • 您期望的结果不是文本,而是某种形式的图形渲染,难以从代码中分析。首先生成文本输出会更容易调试。

    您的代码中有更多问题:

    • main() 中,Tree root = uniteForest(root, forest, 6); 将未定义的变量root 传递给uniteForest
    • Tree uniteForest(Tree root, Tree *forest, int n) 中的参数 root 从未使用过,它仅用于存储临时结果。您应该删除参数并将代码简化为:

      Tree uniteForest(Tree *forest, int n) {
          for (int i = 0; i < n; i++) {
              if (forest[i])
                  uniteTries(forest[i], forest[i]->parent);
          }
          return forest[0];
      }
      
    • main 仅打印树的根,因此递归地打印 forest[0] 及其后代的值。相反,您想打印节点的值及其直接子节点的值,然后为每个子节点递归。

    这是一个更正的版本:

    void printParentChildRec(Tree node) {
        if (node) {
            printf("%d ", node->value);
            for (Tree child = node->child; child; child = child->sibling) {
                printf("%d ", child->value);
            }
            printf("\n");
            for (Tree child = node->child; child; child = child->sibling) {
                printParentChildRec(child);
            }
        }
    }
    

    输出:

    0 1 2 3 1 4 5 4 5 2 3

    【讨论】:

    • 你好,对不起,但这只是我的一个固定,“将指针隐藏在 typdef 后面”,生成的图像只是为了让您获得一些视觉输出。如果您只调用我在下面给出的函数,则原始输出将是。
    • @A.Cretan:你真的坚持要在 typedef 后面隐藏指针吗? C 不是 java,将指针隐藏在 typedef 后面是 java 对所有对象所做的,但它没有真正的指针,因此不会造成混淆。当您开始在 C 中使用指向类型定义指针的指针时,事情往往会变得混乱。这只是我的观点,但已被广泛接受。
    • 我理解你的意见,但我真的不认为这是这里的主题,这里的重点是:代码有效,问题陈述已经出现,我只是想获得一些帮助和让我明白这一点,这些事情在被问到的真正问题背后是相当小的事情,我真的明白你在说什么,但是代码正在运行并且运行良好。我希望你能理解我并在这里看到我的观点,因此你的答案不应该是一个真正的答案,而是一个评论
    • @A.Cretan:我承认第一点是意见,第二点是真实的,但次要的,第三点是要求提供更多信息。您的代码不会产生发布的输出,这可能解释了为什么您没有得到更准确的答案。
    • 因为没有调用functino :-??在第一个代码中??你也迷雾了,只需从底部复制它并将其粘贴到另一个并在 main 中调用它???
    【解决方案2】:

    我认为你的联合树功能是这里的问题。我用我的调试器运行了这段代码,这就是它从根目录下的样子

    这是在联合树方法之后输入到打印函数中的根:

    你能告诉我你想在这里实现什么,也许我可以帮助你!

    【讨论】:

    • 是的,我知道这就是它的样子。这就是它应该的样子。它在问题中指定了我想要实现的目标:一个递归函数,它将打印当前节点的“值”以及它的子节点的所有“值”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-24
    • 1970-01-01
    • 1970-01-01
    • 2014-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多