你会如何迭代这个2D链表?

时间:2017-11-04 08:49:04

标签: c doubly-linked-list

你将如何迭代这个2D链表?

typedef struct _NODE 
{
    char          *pszName; 
    unsigned long ulIntVal;  
    char          *pszString; 
    struct _NODE  *pNext;  
    struct _NODE  *pDown; 
} NODE;

我可以做这样的事情......

NODE *pHEad;

while (pHead != NULL) {
    printf("%s", pHead->pDown->pszName);
    pHead = pHead->pNext;
}

..但它只会给我每个下一个节点下的一个节点。如果又是那个节点下的另一个节点怎么办?又在那个下?或者如果pNext附加了pDown

3 个答案:

答案 0 :(得分:3)

在最简单的情况下,您可以使用类似以下递归函数的内容:

void processNode(NODE *current) {
    if (current != NULL) {
        printf("%s", current->pszName);

        processNode(current->pNext);
        processNode(current->pDown);
    }
}

int main(void) {
    NODE *pHead;
    /* ... Do something to fill your list ... */
    processNode(pHead);
    /* ... */
}

另请注意,这可能会导致函数调用的深层嵌套,具体取决于您处理的列表。因此,如果您使用的是具有有限堆栈大小的嵌入式系统,或者如果您正在处理大型列表,则可能会耗尽堆栈。在这种情况下,您应该找到另一种处理方法。

请注意,这将首先处理pNext-list,然后开始处理最后一个节点的pDown-list的第一个节点。因此,假设以下结构(右边是pNext,向下是pDown):

pHead -> p1 -------> p2
         |- p1_1     |- p2_1 -> p2_1_1
         \- p1_2     |- p2_2
                     \- p2_3 -> p2_3_1

它应按以下顺序打印节点:

pHead, p1, p2, p2_1, p2_1_1, p2_2, p2_3, p2_3_1, p1_1, p1_2

答案 1 :(得分:0)

看看这个答案。不要被代码量淹没。我添加了足够的评论来帮助您继续。

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

typedef struct Node{
    char data[100]; // Assume that this linked list will contain only 100 chars of data
    struct Node* next;
} NODE;

// Global Variables are bad, but oh well.
NODE* head = NULL;

// Function to create a node
NODE* createNode(char* str)
{
    // First allocate memory for struct
    NODE* newNode = malloc(sizeof(NODE));
    if(newNode == NULL)
    {
        printf("Unable to create a new node.");
    }
    else
    {

        // Use strcpy or strncpy or memcpy instead of doing something like newNode -> data = str, which changes the pointer, but doesn't copy the contents
        // That is do not do newNode -> data = "hello" or something
        strncpy(newNode -> data, str, strlen(str));
        newNode -> next = NULL;
    }
    return newNode;
}

void addNode(char* str)
{
    // Returns a node which contains str, but points to NULL
    NODE* newNode = createNode(str);

    // If the linked list is empty, then we make this node itself as the first node(or head)
    if(head == NULL)
    {
        head = newNode;
    }
    // Else if the linked list is not empty, then we add this node at the start of the linked list
    else
    {
        newNode -> next = head;
        head = newNode;
    }
}

int main()
{
   // Example Linked List Generated(say you already have it in some form)
   addNode("This");
   addNode("Is");
   addNode("Linked List");

   // Now let's print the linked list
   // Temporary NODE pointer ptr is used in order to not mess with the original NODE pointer head.
    NODE* ptr = head; 

    // Traverse through the linked list starting from head and at the same time printing the corresponding data, until ptr is null
    // This ptr != NULL check is exactly what you are looking for. This is your way of stopping the traversal of Linked List once you
    // are at the end of it. You don't have to know the number of nodes to stop the traversal this way.
    while(ptr != NULL)
    {
        printf("%s ", ptr -> data);
        ptr = ptr -> next;
    }
}

但请注意,输出将以相反的顺序打印,因为在链接列表的这个实现中,我们正在向后添加内容。只需尝试运行程序并从main函数开始阅读程序。我已将代码分成单独的函数,以便您更容易理解。只需先运行代码即可掌握所发生的事情。

答案 2 :(得分:0)

如果你想避免堆栈溢出的可能性,你可以通过添加队列来使用迭代而不是递归 - 尽管这会使用更多的堆内存,并且仍然存在可能耗尽的风险堆内存,如果您有一个大型列表或者您是否在内存受限的系统上运行。最重要的部分是最后的print_list功能;其他的东西只是我提供的(大部分)自我管理队列实现:

typedef struct node_queue NodeQueue;
struct node_queue {
    NODE *n;
    NodeQueue *next;
};

/*
 * Add an item to the end of the queue.
 *
 * If the item could not be added, 0 is returned.
 * Otherwise, a nonzero value is returned.
 */
int enqueue(NodeQueue **headp, NodeQueue **endp, NODE *n)
{
    NodeQueue *old_end = *endp;
    NodeQueue *new_end;

    new_end = malloc(sizeof *new_end);
    if (new_end == NULL) {
        return 0;
    }
    new_end->n = n;
    new_end->next = NULL;

    if (old_end != NULL) {
        old_end->next = new_end;
    }
    if (*headp == NULL) {
        *headp = new_end;
    }
    *endp = new_end;
    return 1;
}

/*
 * Remove an item from the head of the queue,
 * storing it in the object that "nret" points to.
 *
 * If no item is in the queue, 0 is returned.
 * Otherwise, a nonzero value is returned.
 */
int dequeue(NodeQueue **headp, NodeQueue **endp, NODE **nret)
{
    NodeQueue *old_head = *headp;
    NodeQueue *new_head;
    if (old_head == NULL) {
        return 0;
    }
    if (nret != NULL) {
        *nret = old_head->n;
    }
    new_head = old_head->next;
    free(old_head);
    if (new_head == NULL) {
        *endp = NULL;
    }
    *headp = new_head;
    return 1;
}

void print_list(NODE *start)
{
    NodeQueue *head = NULL;
    NodeQueue *end = NULL;
    NODE *current;

    current = start;

    /* Iterate all `pNext` nodes, then pop each `pDown` node and repeat. */
    for (;;) {
        /* Add the "down" node to the node queue. */
        if (current->pDown != NULL) {
            if (!enqueue(&head, &end, current->pDown)) {
                perror("warning: could not add node to queue");
            }
        }

        printf("%s", current->pszNode);

        /*
         * Move to the "next" node.
         * If there is no next node, get the first "down" node from the queue.
         * If there is no "down" node, break the loop to end processing.
         */
        current = current->pNext;
        if (current == NULL) {
            if (!dequeue(&head, &end, &current)) {
                break;
            }
        }
    }
}

这将迭代所有pNext项目,然后转移到pDown项目。以下二维列表将打印为A B C D E F G H I J K L M N O P Q

A
|
B--C
|
D--E-----------F
   |           |
   G-----H     I-----J
   |     |     |     |
   K--L  M--N  O     P
         |
         Q

您可以通过在其中pDownpNext进行交换来反转print_list / pNext pDown功能的优先级,因此pNext项目被添加到队列中,pDown项目将被迭代,直到用尽为止,这将更改项目打印的顺序为A B D C E G K F I O H M Q L J P N,除非您更改列表的结构。

您可以使用上面的代码和https://repl.it/NjyV/1上面的第一个示例2-D链接列表查看示例,但我更改了NODE的定义以使代码使用其字段简单。