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

时间:2015-10-23 12:29:16

标签: 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, "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循环,它可以迭代内部节点,直到找到NULL,然后创建新节点。我遇到的问题是,当我遍历节点时,指针地址会发生变化,而我留下了一个不再有效的大混乱。

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

问题是我必须将子节点添加到列表中,所以我将改变各种指针地址但是要做到这一点看起来我需要一个列表的副本,我可以在其中使用它以便原始地址不会改变。

如何遍历子节点并创建新节点,以便我不必对一堆IF语句进行硬编码?

2 个答案:

答案 0 :(得分:1)

我的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;        

}

答案 1 :(得分:1)

您的链接列表更像是树:

|-> MUSIC
|   |-> Punk
|   |-> Funk
|-> VIDEOS
|   |-> Cats
|   |-> Cars
|   |-> Parties
|-> PICTURES
|   |-> 2014
|   |   |-> Skiing
|   |   |-> Trip
|   |   |-> Thksgvg
|   |-> 2015
|   |   |-> Birthday
|   |   |-> Wedding

您通过pNext转到垂直线上的下一个节点(下一个最老的兄弟姐妹),然后通过pInner进入更深层次(最大的孩子)。所有年幼的兄弟姐妹和节点的所有孩子都可以通过节点指针访问。你也可以指向一个指向parant的指针,这样你就可以走上去,而不仅仅是向下走。

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

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更改为pNextNULL的新节点。只有在追加第一个节点时,才能确保更改列表的头部。同样,仅在添加第一个子节点时更改节点的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;
}