Fundamentals 25 min read

Master Linked Lists in C: From Arrays to Circular Lists

This article explains the differences between array‑based sequential storage and pointer‑based linked storage, introduces singly, doubly and circular linked lists, and provides complete C implementations for creation, insertion, deletion, and traversal with clear diagrams and step‑by‑step code examples.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Master Linked Lists in C: From Arrays to Circular Lists

Sequential Storage vs. Linked Storage

Arrays store elements contiguously, which makes random access fast but causes costly element shifting during insertions and deletions. An example shows how inserting {1,2,3,4} after the first element requires moving existing elements and then overwriting the target position.

Insert requires moving many elements, wasting CPU cycles.

Array size must be pre‑allocated to avoid overflow.

Linked List – Linked Storage

Linked lists overcome array drawbacks by using non‑contiguous memory and pointers, allowing dynamic growth without massive data movement. Each node contains a data field and a next pointer that links to the following node.

Insertion and deletion only require updating pointers, as illustrated by the following diagrams.

While linked lists save time on modifications, they consume more memory for the pointer fields.

Linked List Overview

Common variants include singly linked list, doubly linked list, and circular singly linked list. Their structures are similar, differing mainly in pointer arrangement.

Singly linked list : each node points only to its successor.

Doubly linked list : each node has both prev and next pointers.

Circular linked list : the last node points back to the head, forming a loop.

Singly Linked List

Concept and Node Design

A node is defined as:

typedef struct Node {
    int data;            // data can be any type, including structs
    struct Node *next;   // pointer to the next node
} Node, *LinkedList;

Initialization

Allocate a head node and set its next to NULL. Always check the return value of malloc to avoid dereferencing a null pointer.

LinkedList listinit(){
    Node *L = (Node*)malloc(sizeof(Node));
    if (L == NULL) {
        printf("Allocation failed");
        exit(0);
    }
    L->next = NULL;
    return L;
}

Head‑Insertion Method

New nodes are inserted at the front, resulting in a reversed order of input.

LinkedList LinkedListCreatH(){
    Node *L = (Node*)malloc(sizeof(Node));
    L->next = NULL;
    int x;
    while (scanf("%d", &x) != EOF) {
        Node *p = (Node*)malloc(sizeof(Node));
        p->data = x;
        p->next = L->next;
        L->next = p;
    }
    return L;
}

Tail‑Insertion Method

Maintains a tail pointer r so that the resulting list preserves the input order.

LinkedList LinkedListCreatT(){
    Node *L = (Node*)malloc(sizeof(Node));
    L->next = NULL;
    Node *r = L;
    int x;
    while (scanf("%d", &x) != EOF) {
        Node *p = (Node*)malloc(sizeof(Node));
        p->data = x;
        r->next = p;
        r = p;
    }
    r->next = NULL;
    return L;
}

Traversal, Printing and Modification

Traverse from the head, printing each node’s data. Modification searches for a target value and updates it.

void printList(LinkedList L){
    Node *p = L->next;
    int i = 0;
    while (p) {
        printf("Element %d: %d
", ++i, p->data);
        p = p->next;
    }
}

LinkedList LinkedListReplace(LinkedList L, int x, int k){
    Node *p = L->next;
    while (p) {
        if (p->data == x) p->data = k;
        p = p->next;
    }
    return L;
}

Insertion at Position i

Locate the predecessor node, allocate a new node, adjust pointers, and return the updated list.

LinkedList LinkedListInsert(LinkedList L, int i, int x){
    Node *pre = L;
    for (int j = 1; j < i; ++j) pre = pre->next;
    Node *p = (Node*)malloc(sizeof(Node));
    p->data = x;
    p->next = pre->next;
    pre->next = p;
    return L;
}

Deletion by Value

Find the node containing the target value, link its predecessor to its successor, then free the node.

LinkedList LinkedListDelete(LinkedList L, int x){
    Node *pre = L;
    Node *p = L->next;
    while (p && p->data != x) {
        pre = p;
        p = p->next;
    }
    if (p) {
        pre->next = p->next;
        free(p);
    }
    return L;
}

Doubly Linked List

Concept

Each node stores both a pre (previous) and next pointer, allowing traversal in both directions and eliminating the need to restart from the head for reverse searches.

typedef struct line {
    int data;
    struct line *pre;
    struct line *next;
} line, *a;

Creation

Prompt the user for the number of nodes, create the head node, then iteratively allocate new nodes, linking them forward and backward.

line* initLine(line *head){
    int number, pos = 1, input_data;
    printf("Enter number of nodes: ");
    scanf("%d", &number);
    if (number < 1) return NULL;
    head = (line*)malloc(sizeof(line));
    head->pre = NULL;
    head->next = NULL;
    printf("Input data for node %d: ", pos++);
    scanf("%d", &input_data);
    head->data = input_data;
    line *list = head;
    while (pos <= number) {
        line *body = (line*)malloc(sizeof(line));
        body->pre = NULL;
        body->next = NULL;
        printf("Input data for node %d: ", pos++);
        scanf("%d", &input_data);
        body->data = input_data;
        list->next = body;
        body->pre = list;
        list = list->next;
    }
    return head;
}

Insertion

Allocate a new node, set its pre and next pointers appropriately, and adjust surrounding nodes.

line* insertLine(line *head, int data, int add){
    line *temp = (line*)malloc(sizeof(line));
    temp->data = data;
    temp->pre = NULL;
    temp->next = NULL;
    if (add == 1) {
        temp->next = head;
        head->pre = temp;
        head = temp;
    } else {
        line *body = head;
        for (int i = 1; i < add-1; ++i) body = body->next;
        if (body->next == NULL) {
            body->next = temp;
            temp->pre = body;
        } else {
            body->next->pre = temp;
            temp->next = body->next;
            body->next = temp;
            temp->pre = body;
        }
    }
    return head;
}

Deletion

Locate the node with the target value, reconnect its predecessor and successor, then free it.

line* deleteLine(line *head, int data){
    line *list = head;
    while (list) {
        if (list->data == data) {
            list->pre->next = list->next;
            if (list->next) list->next->pre = list->pre;
            free(list);
            printf("--Deletion successful--
");
            return head;
        }
        list = list->next;
    }
    printf("Error: element not found
");
    return head;
}

Traversal

Iterate using the next pointer and print each node’s data.

void printLine(line *head){
    line *list = head;
    int pos = 1;
    while (list) {
        printf("Node %d data: %d
", pos++, list->data);
        list = list->next;
    }
}

Circular Singly Linked List

Concept

The last node’s next points back to the head, forming a closed loop.

typedef struct list {
    int data;
    struct list *next;
} list;

Initialization

list* initlist(){
    list *head = (list*)malloc(sizeof(list));
    if (head == NULL) {
        printf("Creation failed, exiting
");
        exit(0);
    }
    head->next = NULL;
    return head;
}

After creating the head, set head->next = head to close the loop.

Creation (Insertion)

int insert_list(list *head){
    int data;
    printf("Enter element to insert: ");
    scanf("%d", &data);
    list *node = initlist();
    node->data = data;
    if (head != NULL) {
        list *p = head;
        while (p->next != head) p = p->next;
        p->next = node;
        node->next = head;
        return 1;
    } else {
        printf("Head is empty
");
        return 0;
    }
}

Insertion at Position

list* insert_list(list *head, int pos, int data){
    list *node = initlist();
    node->data = data;
    if (head != NULL) {
        list *t = head;
        for (int i = 1; i < pos; ++i) t = t->next;
        node->next = t->next;
        t->next = node;
        return head;
    }
    return head;
}

Deletion

int delete_list(list *head){
    if (head == NULL) {
        printf("List is empty!
");
        return 0;
    }
    list *temp = head;
    list *ptr = head->next;
    int del;
    printf("Enter element to delete: ");
    scanf("%d", &del);
    while (ptr != head) {
        if (ptr->data == del) {
            if (ptr->next == head) {
                temp->next = head;
                free(ptr);
                return 1;
            }
            temp->next = ptr->next;
            free(ptr);
            return 1;
        }
        temp = temp->next;
        ptr = ptr->next;
    }
    printf("Element not found
");
    return 0;
}

Traversal

int display(list *head){
    if (head != NULL) {
        list *p = head;
        while (p->next != head) {
            printf("%d   ", p->next->data);
            p = p->next;
        }
        printf("
");
        return 1;
    } else {
        printf("Head is NULL!
");
        return 0;
    }
}

Advanced Concept – Doubly Circular Linked List

The doubly circular list combines the bidirectional pointers of a doubly linked list with the circular connection of the tail to the head, enabling efficient forward and backward traversal in a loop.

Summary of Linked Lists

Array‑based sequential tables require shifting many elements during insertions or deletions, leading to O(n) time and the need for pre‑allocated space. Linked lists eliminate these drawbacks by using pointer‑based dynamic storage, offering O(1) insertion and deletion at known positions while incurring extra memory for pointers. Understanding the trade‑offs helps developers choose the appropriate structure for their projects.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

algorithmCData StructureArraylinked listpointer
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.