How to create a doubly linked list in C

1 Answer

0 votes
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <stdbool.h>

/*
 * A minimal doubly‑linked list node.
 * It contains only pointers to the previous and next nodes.
 * The actual data will be stored in a struct that embeds this node.
 */
struct DNode {
    struct DNode *previous;
    struct DNode *next;
};

/*
 * A List contains two sentinel nodes:
 *   head — marks the beginning (does not store data)
 *   tail — marks the end (does not store data)
 *
 * These sentinels simplify insertion/removal logic because
 * the list is never truly empty — the sentinels always exist.
 */
struct List {
    struct DNode head;   // sentinel head
    struct DNode tail;   // sentinel tail
};

typedef struct DNode *NODE;
typedef struct List *LIST;

/*
 * Create and initialize an empty list.
 * The head points forward to the tail.
 * The tail points backward to the head.
 */
LIST newList(void) {
    LIST l = malloc(sizeof(struct List));
    if (!l) return NULL;

    l->head.previous = NULL;      // head has no previous
    l->head.next = &l->tail;      // head → tail

    l->tail.previous = &l->head;  // tail ← head
    l->tail.next = NULL;          // tail has no next

    return l;
}

/*
 * A list is empty when head.next points directly to tail.
 */
bool isEmpty(LIST l) {
    return l->head.next == &l->tail;
}

/*
 * Return the first real node (NULL if empty).
 */
NODE getHead(LIST l) {
    return isEmpty(l) ? NULL : l->head.next;
}

/*
 * Return the last real node (NULL if empty).
 */
NODE getTail(LIST l) {
    return isEmpty(l) ? NULL : l->tail.previous;
}

/*
 * Insert a node immediately after the head sentinel.
 */
NODE addHead(LIST l, NODE n) {
    NODE first = l->head.next;

    n->previous = &l->head;
    n->next = first;

    first->previous = n;
    l->head.next = n;

    return n;
}

/*
 * Insert a node immediately before the tail sentinel.
 */
NODE addTail(LIST l, NODE n) {
    NODE last = l->tail.previous;

    n->previous = last;
    n->next = &l->tail;

    last->next = n;
    l->tail.previous = n;

    return n;
}

/*
 * Remove and return the first real node.
 */
NODE removeHead(LIST l) {
    if (isEmpty(l)) return NULL;

    NODE n = l->head.next;   // first real node
    NODE next = n->next;

    l->head.next = next;
    next->previous = &l->head;

    return n;
}

/*
 * Remove and return the last real node.
 */
NODE removeTail(LIST l) {
    if (isEmpty(l)) return NULL;

    NODE n = l->tail.previous;  // last real node
    NODE prev = n->previous;

    prev->next = &l->tail;
    l->tail.previous = prev;

    return n;
}

/*
 * Insert node n after node r.
 * Assumes r is already in the list.
 */
NODE insertAfter(LIST l, NODE r, NODE n) {
    NODE s = r->next;

    n->previous = r;
    n->next = s;

    s->previous = n;
    r->next = n;

    return n;
}

/*
 * Remove a specific node from the list.
 * Assumes n is in the list.
 */
NODE removeNode(LIST l, NODE n) {
    NODE p = n->previous;
    NODE s = n->next;

    p->next = s;
    s->previous = p;

    return n;
}

/*
 * A user-defined node that stores actual data.
 * It embeds a DNode so it can be linked into the list.
 */
struct IntNode {
    struct DNode node;  // embedded list node
    int data;           // payload
};

/*
 * Print list from head → tail.
 */
void printForward(LIST l) {
    NODE n = l->head.next;
    printf("Print Forward: ");
    while (n != &l->tail) {
        struct IntNode *m = (struct IntNode *)n;
        printf("%d ", m->data);
        n = n->next;
    }
    printf("\n");
}

/*
 * Print list from tail → head.
 */
void printBackward(LIST l) {
    NODE n = l->tail.previous;
    printf("Print Backward: ");
    while (n != &l->head) {
        struct IntNode *m = (struct IntNode *)n;
        printf("%d ", m->data);
        n = n->previous;
    }
    printf("\n");
}

int main() {
    srand((unsigned)time(NULL));  // seed random generator

    LIST l = newList();
    if (!l) return 1;

    // Add 5 random nodes at tail
    for (int i = 0; i < 5; i++) {
        struct IntNode *n = malloc(sizeof(struct IntNode));
        n->data = rand() % 100;   // random number 0–99
        addTail(l, (NODE)n);
    }
    puts("Add 5 nodes at tail");

    printForward(l);
    printBackward(l);

    // Insert a node at head
    struct IntNode *h = malloc(sizeof(struct IntNode));
    h->data = 999;
    addHead(l, (NODE)h);
    puts("Insert 999 at head");

    printForward(l);

    // Remove head node
    struct IntNode *rh = (struct IntNode *)removeHead(l);
    printf("Removed head: %d\n", rh->data);
    free(rh);

    printForward(l);

    // Remove all nodes from tail
    while (!isEmpty(l)) {
        struct IntNode *n = (struct IntNode *)removeTail(l);
        printf("Removed tail: %d\n", n->data);
        free(n);
    }

    free(l);
    
    return 0;
}


/*
run:

Add 5 nodes at tail
Print Forward: 67 16 95 6 50 
Print Backward: 50 6 95 16 67 
Insert 999 at head
Print Forward: 999 67 16 95 6 50 
Removed head: 999
Print Forward: 67 16 95 6 50 
Removed tail: 50
Removed tail: 6
Removed tail: 95
Removed tail: 16
Removed tail: 67

*/

 



answered Apr 15 by avibootz
edited Apr 15 by avibootz
...