How would you iterate this 2D linked list?

Viewed 740

How would you iterate this 2D linked list?

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

I could do something like this..

NODE *pHEad;

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

.. but it would only give me the one node under every next node. What if it is another node under that one again? And under that one again? Or if there is a pNext attached to the pDown?

3 Answers

In the simplest case, you could use something like the following recursive function:

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);
    /* ... */
}

Also be aware that this can cause a deep nesting of the function calls depending on your processed list. So if you are on an embedded system with limited stack size or if you are processing huge lists, you might run out of stack. In that case, you should find another approach for the processing.

Note that this will first process the pNext-list and then start with processing the first node of the pDown-list of the last node. So assuming the following structure (to the right is pNext and downwards is pDown):

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

it should print the nodes in the following order:

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

Look at this answer. Don't be overwhelmed by the amount of the code. I have added enough comments to help you proceed.

#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;
    }
}

However note that the output will be printed in reverse order, since in this implementation of linked list we are adding things towards the back. Just try running the program and start reading the program starting from main function. I have made the code into separate functions to make it easier for you to understand. Just run the code first to get a grasp of what's happening.

You can use iteration instead of recursion by adding a queue, too, if you want to avoid the possibility of a stack overflow—though this will use slightly more heap memory, and there is still a risk that you can run out of heap memory if you have a large list or if you're running on a memory-constrained system. The important part is the print_list function at the end; the other stuff is just a (mostly) self-managing queue implementation I've provided:

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;
            }
        }
    }
}

This will iterate through all pNext items before moving to a pDown item. The following 2-D list will be printed as 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

You can reverse the priority of pDown/pNext in the print_list function by swapping pNext and pDown inside it, so pNext items are added to the queue and pDown items are iterated until exhausted, which will change the order in which the items are printed to A B D C E G K F I O H M Q L J P N unless you change the structure of the list.

You can see an example using both the code above and the first sample 2-D linked list above at https://repl.it/NjyV/1, though I changed the definition of NODE to make the code using its fields a bit simpler.

Related