Traversal (Displaying Nodes)

In a singly linked list (SLL), traversal means visiting each node from head until next is NULL. Below: iterative traversal (loop) and recursive traversal to print node values. Time O(n); iterative space O(1), recursive space O(n) for the call stack.

1) Iterative traversal (C)

Use a pointer cur that starts at head and moves with cur = cur->next until cur == NULL.

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

struct Node {
    int data;
    struct Node *next;
};

/* Walk the list and print each data value */
void traverse_iterative(struct Node *head)
{
    struct Node *cur = head;
    while (cur != NULL) {
        printf("%d ", cur->data);
        cur = cur->next;
    }
    printf("\n");
}

int main(void)
{
    struct Node *n1 = malloc(sizeof(struct Node));
    struct Node *n2 = malloc(sizeof(struct Node));
    struct Node *n3 = malloc(sizeof(struct Node));
    if (!n1 || !n2 || !n3) return 1;

    n1->data = 10; n1->next = n2;
    n2->data = 20; n2->next = n3;
    n3->data = 30; n3->next = NULL;

    struct Node *head = n1;
    traverse_iterative(head);   /* output: 10 20 30 */

    free(n1); free(n2); free(n3);
    return 0;
}

Program output

Running the program prints the three values on one line, then a newline:

10 20 30

Step-by-step execution

Step cur points to cur != NULL? Action Output Next cur
Initial head (node n1) Yes Print cur->data 10 cur = n1->next (n2)
2 n2 Yes Print cur->data 20 cur = n2->next (n3)
3 n3 Yes Print cur->data 30 cur = n3->next (NULL)
4 NULL No Exit loop (none) Loop ends
After loop printf("\n") newline Function returns

Memory layout (example addresses)

Variable Address (example) data next Points to
n1 0x1000 10 0x2000 n2
n2 0x2000 20 0x3000 n3
n3 0x3000 30 NULL (0x0) nowhere

Function call trace (iterative loop)

Line executed cur value State
struct Node *cur = head; points to n1 Initialization
while (cur != NULL) n1 (0x1000) Condition true
printf("%d ", cur->data); n1 Prints 10
cur = cur->next; n2 (0x2000) Move to next node
while (cur != NULL) n2 (0x2000) Condition true
printf("%d ", cur->data); n2 Prints 20
cur = cur->next; n3 (0x3000) Move to next node
while (cur != NULL) n3 (0x3000) Condition true
printf("%d ", cur->data); n3 Prints 30
cur = cur->next; NULL (0x0) Move past last node
while (cur != NULL) NULL Condition false, exit loop
printf("\n"); Prints newline

Key observations

Aspect Explanation
Order preserved Output follows insertion order: 10, then 20, then 30.
Spaces between numbers The format "%d " prints a space after each integer.
Newline printf("\n") moves the cursor to the next line after the loop.
List unchanged Traversal only reads nodes; cur is temporary and does not modify the list.

Critical points

What Why
Check allocations if (!n1 || !n2 || !n3) return 1; avoids using failed malloc results.
Last next == NULL Acts as the sentinel so the loop stops after the last node.
free() every node Releases heap memory and prevents leaks (here, three free calls).
No use-after-free here Traversal finishes before free; do not dereference nodes after freeing.

In short, the program prints the three data values 10, 20, and 30 on one line with spaces between them, then ends the line with a newline.

2) Recursive traversal — display SLL values (C)

Base case: empty list (p == NULL). Otherwise print current node, then recurse on p->next. This prints in forward order (same as the loop above).

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

struct Node {
    int data;
    struct Node *next;
};

void display_recursive(struct Node *p)
{
    if (p == NULL)
        return;
    printf("%d ", p->data);
    display_recursive(p->next);
}

int main(void)
{
    struct Node *n1 = malloc(sizeof(struct Node));
    struct Node *n2 = malloc(sizeof(struct Node));
    struct Node *n3 = malloc(sizeof(struct Node));
    if (!n1 || !n2 || !n3) return 1;

    n1->data = 10; n1->next = n2;
    n2->data = 20; n2->next = n3;
    n3->data = 30; n3->next = NULL;

    display_recursive(n1);   /* output: 10 20 30 */
    printf("\n");

    free(n1); free(n2); free(n3);
    return 0;
}

Program output

The recursive program prints the same line as the iterative version, then a newline from main:

10 20 30

Recursive function call stack

Call # p points to p == NULL? Action Output Next action
1 n1 (data = 10) No printf p->data 10 Call display_recursive(n1->next)
2 n2 (data = 20) No printf p->data 20 Call display_recursive(n2->next)
3 n3 (data = 30) No printf p->data 30 Call display_recursive(n3->next)
4 NULL Yes return; (base case) (none) Unwind to caller of call #3

Complete call stack trace (with recursion depth)

Step Depth Function / phase p value Operation Accumulated output
10display_recursive(n1)n1 (0x1000)Check p != NULL""
20display_recursive(n1)n1Print 10"10 "
31display_recursive(n2)n2 (0x2000)Check p != NULL"10 "
41display_recursive(n2)n2Print 20"10 20 "
52display_recursive(n3)n3 (0x3000)Check p != NULL"10 20 "
62display_recursive(n3)n3Print 30"10 20 30 "
73display_recursive(NULL)NULLCheck p == NULL"10 20 30 "
83display_recursive(NULL)NULLreturn; (no print)"10 20 30 "
92Return to call on n3Function ends"10 20 30 "
101Return to call on n2Function ends"10 20 30 "
110Return to call on n1Function ends"10 20 30 "
12printf("\n") in mainPrint newlineline ends

Memory layout (same list as iterative example)

Node Address data next Points to
n1 0x1000 10 0x2000 n2
n2 0x2000 20 0x3000 n3
n3 0x3000 30 NULL (0x0) nowhere

Visual call tree

Call tree for display_recursive: print each node then recurse to next until NULL
Recursive calls: print then display_recursive(p->next) until base case.
Call stack growth while recursively traversing a singly linked list
Activation records on the call stack as recursion deepens (conceptual).

Timing: when prints happen

Phase t1 t2 t3 t4 t5
Call stack (concept) disp(n1) disp(n1)→disp(n2) disp(n3) disp(NULL) Unwinding returns
Action Print 10 Print 20 Print 30 Base case: no print Return through levels
Output so far 10 10 20 10 20 30 10 20 30 10 20 30

Recursive vs iterative

Aspect Recursive (display_recursive) Iterative (traverse_iterative)
Extra memory O(n) call stack (here, 3 frames deep) O(1) (one cur pointer)
Risk Stack overflow if the list is extremely long No recursion stack growth
Style Compact, matches recursive definitions Straightforward loop
Output order Forward: 10 → 20 → 30 Forward: 10 → 20 → 30
Stop condition if (p == NULL) return; while (cur != NULL)
When it prints Before each recursive call (pre-order walk) Each loop iteration

Key observations (recursive)

Point Explanation
Print before recurse Prints the current node, then visits p->next (pre-order style for this print function).
Base case When p == NULL, return without printing—stops deeper calls.
Same line as iterative Both approaches emit 10 20 30 for this list.
Newline in main printf("\n") after display_recursive(n1) finishes adds the line break.
void is enough No return value is needed because the function only prints.
Important: Recursion is fine for small lists (like three nodes). For very large lists, deep recursion can exhaust the call stack; the iterative version is usually safer when the length is unknown or huge.

Reverse-order display (without reversing the list) swaps the two lines inside display_recursive: recurse first, then printf—that is covered in the reverse-traversal topic.

3) FAQ: Is void valid when the function uses return?

Question
void display_recursive(struct Node *p)
{
    if (p == NULL)
        return;
    printf("%d ", p->data);
    display_recursive(p->next);
}

Is void valid here when I have a return keyword in the function?

Answer: Yes, void is correct here. This is a typical recursive routine that walks the list and prints each node’s data without returning a value to the caller.

Why void fits

  1. The function does work, not a computed result. It prints (a side effect). It does not need to return a value. Recursive calls drive the walk, not an accumulated return value.
  2. The base case uses bare return; (not return expression). That simply exits the function and returns nothing—which matches a void return type:
    if (p == NULL)
        return;   /* exit; no value */
    

If you used a non-void type (e.g. int)

You could write something like this, but it is awkward and misleading for “print only”:

int display_recursive(struct Node *p)  /* misleading for this job */
{
    if (p == NULL)
        return 0;
    printf("%d ", p->data);
    return display_recursive(p->next);
}
  • The return value is not needed for printing.
  • Callers may wrongly assume they should use the returned int.
  • It adds complexity without benefit for this pattern.