当前位置: 首页 > 知识库问答 >
问题:

C使用结构指针递归构建树

史涵育
2023-03-14

我现在正在实现模拟N体问题的Barnes-Hut算法。我只想问关于建筑树的部分。

我做了两个函数来为它构建树。

我递归地构建树,并在构建时打印每个节点的数据,一切看起来都是正确的,但当程序返回到主函数时,只有树的根和根的子节点存储值。其他节点的值没有被存储,这很奇怪,因为我在递归过程中打印了它们,它们应该被存储。

这是经过修改的代码的一部分,我认为问题可能在哪里:

#include<...>
typedef struct node{
    int data;
    struct node *child1,*child2;
}Node;
Node root; // a global variable
int main(){
    .
    set_root_and_build(); // is called not only once cuz it's actually in a loop
    traverse(&root);
    .
}

下面是函数set_root_and_build():

我已经将子指针设置为NULL,但是一开始并没有显示出来。

void set_root_and_build(){
    root.data = ...;
    ..// set child1 and child2 =NULL;
    build(&root,...); // ... part are values of data for it's child 
}

并构建:

void build(Node *n,...){
    Node *new1, *new2 ;
    new1 = (Node*)malloc(sizeof(Node));
    new2 = (Node*)malloc(sizeof(Node));
    ... // (set data of new1 and new2 **,also their children are set NULL**)
    if(some condition holds for child1){ // else no link, so n->child1 should be NULL
        build(new1,...);
        n->child1 = new1;
        //for debugging, print data of n->child1 & and->child2
    }
    if(some condition holds for child2){ // else no link, so n->child2 should be NULL
        build(new2,...);
        n->child1 = new2;
        //for debugging, print data of n->child1 & and->child2
    }
}

树中的节点可能有1~2个孩子,这里不是所有的节点都有2个孩子。

程序在构建()函数递归中打印出正确的数据,但当它返回到main函数并调用遍历()时,由于分段错误而失败。

我尝试在traverse()中打印所有内容,发现只有根和根。child1,根。child2存储的值与我提到的一样。

由于我必须多次调用Build(),即使并行调用,new1和new2也不能定义为全局变量。(但我不认为它们会导致这里的问题)。

有人知道哪里出错了吗?

带有调试信息的遍历部分:

void traverse(Node n){
    ...//print out data of n
    if(n.child1!=NULL)
        traverse(*(n.child1))
    ...//same for child2
}

共有2个答案

庄康胜
2023-03-14

遍历树的问题是,在找到一个NULL节点指针之前,您必须处理树。

不幸的是,当您创建节点时,这些节点既不是用malloc()初始化的,也不是用new初始化的(它将用calloc(()<-code>初始化,但cpp代码中的这种做法与maloc()一样糟糕)。因此,您的遍历将继续在随机指针的梦幻世界中循环/递归。

我建议你利用cpp的优势,稍微改变你的结构:

struct Node {   // that's C++:  no need for typedef
    int data;
    struct node *child1,*child2;
    Node() : data(0), child1(nullptr), child2(nullptr) {} // Makes sure that every created are first initalized
};

然后再把你的老马洛克扔掉。并构造代码以避免不必要的分配:

if(some condition holds for child1){ // else no link, so n->child1 should be NULL
    new1=new Node; // if you init it here, no need to free in an else !!
    build(new1,...);
    n->child1 = new1;
    ...
}
if (... child2) { ... }

但是,请注意,使用< code>new分配的点应使用< code>delete释放,并使用< code>free()注释。

编辑:您的代码片段不匹配:

traverse(&root);  // you send here a Node* 

void traverse(Node n){  // but your function defines an argument by value !
   ...
}

检查你没有忽略来自编译器的一些警告,并且你的代码中没有滥用强制转换。

堵鸿光
2023-03-14

当条件不成立时,您可能没有正确设置n的子级。您可能需要这样做:

void set_root_and_build()
{
    root.data = ...;
    build(&root,...); // ... part are values of data for it's child 
}

void build(Node *n,...)
{
    n->child1 = n->child2 = NULL;

    Node *new1, *new2;
    new1 = (Node*) malloc(sizeof(Node));
    new2 = (Node*) malloc(sizeof(Node));

    // set data of new1 and new2 somehow (read from stdin?)

    if (some condition holds for new1) 
    {
        n->child1 = new1;
        build(n->child1,...);
        //for debugging, print data of n->child1
    }
    else
      free(new1);  // or whatever else you need to do to reclaim new1

    if (some condition holds for new2)
    {
        n->child2 = new2; 
        build(n->child2,...);
        //for debugging, print data of n->child2
    }
    else
      free(new2);  // or whatever else you need to do to reclaim new2
}

当然,您还应该检查malloc()的返回值并处理错误。

此外,您的遍历有点奇怪,因为它通过复制而不是引用递归。你有理由这么做吗?如果不是,那么也许你想要:

void traverse(Node *n)
{
    ...//print out data of n
    if (n->child1 != NULL)
        traverse(n->child1)
    ...//same for child2
}
 类似资料:
  • 主要内容:获取结构体成员,结构体指针作为函数参数当一个 指针变量指向结构体时,我们就称它为 结构体指针。 C语言结构体指针的定义形式一般为: struct 结构体名 *变量名; 下面是一个定义结构体指针的实例: 也可以在定义结构体的同时定义结构体指针: 注意,结构体变量名和数组名不同,数组名在表达式中会被转换为数组指针,而结构体变量名不会,无论在任何表达式中它表示的都是整个集合本身,要想取得结构体变量的地址,必须在前面加 ,所以给 pstu 赋

  • 本文向大家介绍使用递归[JavaScript]创建层次结构,包括了使用递归[JavaScript]创建层次结构的使用技巧和注意事项,需要的朋友参考一下 示例 输出            

  • 我正在学习链表,以及如何在C中使用结构和指针创建链表。下面我举一个例子。据我所知,被调用的将头节点所在的结构的开始内存位置作为参数传递。push()函数的参数将结构节点作为指向指针的指针,因此它作为引用传递,而不是实际副本。因此,我们的的第一个指针只是指向头部节点的内存位置的指针,第二个指针指向该值,该值是头部节点指向的下一个内存位置。我们通过为结构节点分配一些内存,在结构节点内创建一个名为new

  • 问题内容: 我有一个将位置链接在一起的数据库表;一个位置可以在一个位置,也可以在另一个位置内。 这是深入探讨MySQL / PHP的深度: 在给定父级位置的情况下,如何使用MySQL如何获得其所有后代位置,无论深度如何? 问题答案: mysql.com上有 一篇漂亮的文章 ,概述了管理分层数据的各种方法。我认为它为您的问题提供了完整的解决方案,并显示了各种不太简单但较快的方法(例如嵌套集)。

  • 问题内容: 我正在尝试为C库编写SWIG包装器,该包装器使用指向其结构中函数的指针。我不知道如何处理包含函数指针的结构。下面是一个简化的示例。 test.i: 样本会议: 有人知道是否有可能让 t.my_func(1) 返回2吗? 谢谢! 问题答案: 我找到了答案。如果我将函数指针声明为SWIG“成员函数”,则它似乎可以按预期工作: 会议: 我希望不需要编写任何特定于SWIG的自定义代码(我希望仅

  • 问题内容: 我不理解以下代码的行为。在创建作为结构指针切片的匹配结构列表时,代码始终会打印原始数组的最后一个元素(实际上不是匹配项),它会打印12和12。但是,如果将匹配项更改为[]窗口小部件代替[] * Widget,然后将输出10和11。 为什么是这样? 问题答案: 那是因为当您使用指针时,您将添加到数组。 请注意,实际上这是循环中使用的局部变量,因此,这不是您要添加到数组中的地址。 (即使变