Linked Lists in C — Linear, Circular, Singly, and Doubly

Linked Lists in C — Linear, Circular, Singly, and Doubly

Linked Lists in C — Linear, Circular, Singly, and Doubly

Linked lists are one of the most important and flexible data structures in computer science. Unlike arrays, linked lists allow dynamic memory usage, flexible insertion and deletion, and the ability to grow or shrink as needed. In this blog, we will explore:

  • What is a linked list?
  • Singly vs Doubly Linked Lists
  • Linear vs Circular Structures
  • Code examples in C
  • Where they're used in real life

What Is a Linked List?

A linked list is a collection of nodes where each node contains:

  • data: the value
  • pointer: address of the next (and/or previous) node

Unlike arrays, linked lists do not store elements in contiguous memory, which allows dynamic memory allocation and more flexible data structures.

Singly Linked List

In a singly linked list, each node points to the next node in the sequence. The last node points to NULL.

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

Basic Singly Linked List Operations

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

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

// Insert at beginning
void insertAtHead(struct Node** head, int value) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = value;
    newNode->next = *head;
    *head = newNode;
}

// Print list
void printList(struct Node* head) {
    while (head != NULL) {
        printf("%d -> ", head->data);
        head = head->next;
    }
    printf("NULL\n");
}

int main() {
    struct Node* head = NULL;
    insertAtHead(&head, 10);
    insertAtHead(&head, 20);
    insertAtHead(&head, 30);
    printList(head);
    return 0;
}

Output:

30 -> 20 -> 10 -> NULL

Doubly Linked List

Each node has two pointers — one to the next node and one to the previous. This allows backward traversal as well.

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

Example in C:

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

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

void insertAtHead(struct Node** head, int value) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = value;
    newNode->prev = NULL;
    newNode->next = *head;
    if (*head != NULL)
        (*head)->prev = newNode;
    *head = newNode;
}

void printList(struct Node* head) {
    while (head != NULL) {
        printf("%d <-> ", head->data);
        head = head->next;
    }
    printf("NULL\n");
}

int main() {
    struct Node* head = NULL;
    insertAtHead(&head, 5);
    insertAtHead(&head, 15);
    insertAtHead(&head, 25);
    printList(head);
    return 0;
}

Output:

25 <-> 15 <-> 5 <-> NULL

Circular Linked List

In circular linked lists, the last node points back to the head, forming a ring-like structure. It can be singly or doubly circular.

Example (Singly Circular List):

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

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

void insert(struct Node** head, int value) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = value;

    if (*head == NULL) {
        *head = newNode;
        newNode->next = *head;
    } else {
        struct Node* temp = *head;
        while (temp->next != *head)
            temp = temp->next;
        temp->next = newNode;
        newNode->next = *head;
    }
}

void print(struct Node* head) {
    if (head == NULL) return;
    struct Node* temp = head;
    do {
        printf("%d -> ", temp->data);
        temp = temp->next;
    } while (temp != head);
    printf("(back to head)\n");
}

int main() {
    struct Node* head = NULL;
    insert(&head, 100);
    insert(&head, 200);
    insert(&head, 300);
    print(head);
    return 0;
}

Output:

100 -> 200 -> 300 -> (back to head)

When to Use What?

  • Singly Linked List: Fast forward traversal, simple
  • Doubly Linked List: Bidirectional traversal
  • Circular List: Perfect for circular buffer structures

Real-Life Applications

  • Music playlist rotation (circular linked list)
  • Undo/redo functionality (doubly linked list)
  • Dynamic memory allocation (linked blocks)
  • Job scheduling in OS (circular queue)

Conclusion

Linked lists are dynamic, flexible, and form the backbone of many real-world systems. Understanding them unlocks your ability to build more advanced data structures and improve memory-efficient programs. Learn the variations — singly, doubly, circular — and you’ll be on your way to mastering pointers and low-level thinking in C.

Comments