C 如何遍历链表中的子节点?

C 如何遍历链表中的子节点?,c,pointers,linked-list,nodes,C,Pointers,Linked List,Nodes,我正在开发以下程序: ... typedef struct node_tag{ int id; char pLabel[10]; struct node_tag *pNext; struct node_tag *pInner; }NODE; ... int main(int argc, char** argv) { NODE *list = create_node("start"); add_node(&list,

我正在开发以下程序:

...

typedef struct node_tag{
    int id;
    char pLabel[10];
    struct node_tag *pNext;
    struct node_tag *pInner;
    }NODE;

...

int main(int argc, char** argv)
    {

    NODE *list = create_node("start");
    add_node(&list, "MUSIC");
    add_node(&list, "VIDEOS");
    add_node(&list, "PICTURES");
    create_sub_node( &list, "2015" ); // sub node of PICTURES
    create_sub_node( &list, "Trip" ); // sub node of 2015
    print_nodes(list);

    return (EXIT_SUCCESS);
    }
输出:

|
|-> [ID:4] PICTURES
|   |-> [ID:40] 2015
|   |   |-> [ID:400] Trip
|
|-> [ID:3] VIDEOS
|
|-> [ID:2] MUSIC
|
|-> [ID:1] start
|
从输出中可以看出,到目前为止,一切都按照我的要求工作。但是,我想实现创建子节点的不同方法。我现在得到的是非常有限的,因为它只能创建2个子节点的深度,但我希望有无限的深度:

void create_sub_node( NODE **handle, char* label )
    {
    NODE *new = malloc( sizeof(NODE) );

    new->pNext = NULL;
    new->pInner = NULL;
    strncpy( new->pLabel , label , strlen(label) +1 );   

    if( (*handle)->pInner == NULL )
        {
        new->id = (*handle)->id * 10;
        (*handle)->pInner = new;        
        }
    else if( (*handle)->pInner->pInner == NULL ) 
        {
        new->id = (*handle)->pInner->id * 10;
        (*handle)->pInner->pInner = new;
        }
    }
我尝试实现while循环,该循环可以遍历内部节点,直到找到空节点,然后创建新节点。我遇到的问题是,当我遍历节点时,指针地址发生了变化,留下了一个不再工作的大烂摊子

我尝试复制内部节点的所有地址,但之后无法再次重新分配它们

问题是,我必须将子节点添加到列表中,这样我就可以更改各种指针地址,但要做到这一点,我需要一份列表的副本,在那里我可以使用它,这样原始地址就不会更改


如何迭代子节点并创建新节点,以便不必硬编码一堆IF语句?

我的C有点生疏,但是:

NODE* tail(NODE* list) {
     if (list == NULL) return NULL;
     NODE* current = list;
     while (current->pInner != NULL) { current = current->pInner; }
     return current;              
}
然后,您的函数变成:

void create_sub_node( NODE **handle, char* label )
{
    NODE *new = malloc( sizeof(NODE) );

    new->pNext = NULL;
    new->pInner = NULL;
    strncpy( new->pLabel , label , strlen(label) +1 );   

    NODE* last = tail((*handle));
    new->id = last->id * 10;
    last->pInner = new;        

}

您的链接列表更像一棵树:

|-> MUSIC
|   |-> Punk
|   |-> Funk
|-> VIDEOS
|   |-> Cats
|   |-> Cars
|   |-> Parties
|-> PICTURES
|   |-> 2014
|   |   |-> Skiing
|   |   |-> Trip
|   |   |-> Thksgvg
|   |-> 2015
|   |   |-> Birthday
|   |   |-> Wedding
您可以通过
pNext
进入垂直线上的下一个节点(下一个最年长的兄弟节点),并通过
pInner
进入更深一层(最年长的孩子)。可以通过节点指针访问节点的所有兄弟姐妹和所有子节点。你也可以把一个指针指向巴拉特河,这样你就可以向上走,而不仅仅是向下走

如果您具有创建节点和子节点并返回新节点的函数,则可以轻松构建这样的树:

int main(int argc, char **argv)
{
    NODE *list = NULL;         // list head
    NODE *p;                   // first-generation child 
    NODE *q;                   // second-generation child

    p = add_node(&list, "MUSIC");
        create_sub_node(p, "Punk");
        create_sub_node(p, "Funk");
    p = add_node(&list, "VIDEOS");
        create_sub_node(p, "Cats");
        create_sub_node(p, "Cars");
        create_sub_node(p, "Parties");
    p = add_node(&list, "PICTURES");
    q = create_sub_node(p, "2014");
        create_sub_node(q, "Skiing");
        create_sub_node(q, "Trip");
        create_sub_node(q, "Thksgvg");
    q = create_sub_node(p, "2015");
        create_sub_node(q, "Birthday");
        create_sub_node(q, "Wedding");

    print_nodes(list);

    return (EXIT_SUCCESS);
}
构建树时,必须为新节点的最小同级更改
pNext
,该节点的
pNext
NULL
。确保仅在附加第一个节点时才更改列表的标题。同样,仅当添加第一个子节点时才更改节点的
pInner

通常必须区分两种情况:第一个节点和后续节点。结合这些情况的一种技术是使用指向节点指针的指针遍历列表。该指针首先指向列表或子列表的头部,然后指向节点“
pNext
”,如果有任何节点,即

这里有两个与上述
main
一起使用的函数:

NODE *add_node(NODE **head, const char *label)
{
    NODE **p = head;

    // walk to the end
    while (*p) p = &(*p)->pNext;    

    // append new node
    *p = malloc(sizeof(**p));
    (*p)->pInner = NULL;
    (*p)->pNext = NULL;
    snprintf((*p)->pLabel, sizeof((*p)->pLabel), "%s", label);        

    return (*p);
}

NODE *create_sub_node(NODE *node, const char *label)
{
    NODE **p = &node->pInner;

    // walk to the end
    while (*p) p = &(*p)->pNext;

    // append new node
    *p = malloc(sizeof(**p));    
    (*p)->pInner = NULL;
    (*p)->pNext = NULL;
    snprintf((*p)->pLabel, sizeof((*p)->pLabel), "%s", label);

    return *p;
}

你能告诉我们你试过什么吗?我想你的问题是你试着用
(*handle)=(*handle)->pInner
这样的东西迭代,你应该在那里做
tmp=*handle并在循环中使用
tmp=tmp->pInner
。但是我真的说不出来,因为我没有看到你的代码。你确定你的
create\u sub\u node
函数应该是这样工作的吗?在创建
图片
节点后,如果要创建
音乐
的子节点,该怎么办?如果
add\u node
返回一个节点,然后将该节点传递到
create\u sub\u node
,则If更有意义,而不是让它遍历您的节点。@Groo这是一个有趣的观察结果,但稍后我想修改
create\u sub\u node
函数,使其在我将传递ID的节点下创建子节点。我想如果我朝那个方向走的话,返回节点的整个业务将在主代码中变得混乱。这比我拥有的更少,但我已经尝试了你的代码(我将函数调用中的代码直接添加到
create\u sub\u node
中,它解决了我的问题!也许我的大脑因为花了太多时间而融化了一点,我可能遗漏了什么。还有一个问题:
current=current->pInner
为什么不修改列表的原始内容?我的意思是我知道它是指针的副本,但它仍然指向某个对象,看起来某个对象被修改了,但不在原始列表中。这是为什么?@ShadyProgrammer:
current
只是一个局部变量,它访问列表中的节点,但不修改它们。但在我的函数结束时,我这样做了
current->pInner=new;
哪个更新了我的原始列表,而根据你说的不应该做的?我很困惑。@ShadyProgrammer在while循环的末尾,
current
将包含链接列表中的最后一个元素。
current->pInner
在这种情况下将是
NULL
,因此你正在使
current
指向它的内部po连接到您创建的
新节点。还要注意
->
操作符,该操作符取消引用指针以修改内容(因为当前节点是
节点*
)谢谢你的上述建议。我明白你的意思,但是你不认为给函数
create\u sub\u node
一个要在其下创建子节点的节点ID,而不是为子节点创建和返回变量要干净得多吗?当然,你可以为ID和节点保留一个单独的查找表。(它应该是一个散列或类似的函数。)如果你有一个函数
node\u from\u id()
插入可以用相同的函数完成:
create\u sub\u node(node\u from\u是(99),“Mexico”)
。使用最后一个节点的优点是,您不必查找它;您已经拥有了它。仅供参考,我放弃了将列表视为一个整体并使用复杂循环遍历它的原始方法,而是将头、第1代、第2代等视为彼此链接的单独列表呃,看来我得用一些动态的指针数组来跟踪新一代和新列表。哦,天哪,这会让人毛骨悚然……再次感谢你的建议:)