Delete from Position (Doubly Linked List)

Index pos is 0-based. Use delete from beginning for pos == 0, delete from end for pos == n - 1. In the middle, walk to the predecessor, then cur->next = rm->next and rm->next->prev = cur before free(rm).

C program

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

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

static int count_nodes(struct Node *h)
{
    int c = 0;
    while (h != NULL) {
        c++;
        h = h->next;
    }
    return c;
}

void insert_end(struct Node **head, struct Node **tail, int data)
{
    struct Node *n = malloc(sizeof(struct Node));
    if (n == NULL)
        return;
    n->data = data;
    n->next = NULL;
    if (*head == NULL) {
        n->prev = NULL;
        *head = *tail = n;
        return;
    }
    n->prev = *tail;
    (*tail)->next = n;
    *tail = n;
}

void delete_begin(struct Node **head, struct Node **tail)
{
    if (*head == NULL)
        return;
    struct Node *h = *head;
    if (*head == *tail) {
        free(h);
        *head = *tail = NULL;
        return;
    }
    *head = h->next;
    (*head)->prev = NULL;
    free(h);
}

void delete_end(struct Node **head, struct Node **tail)
{
    if (*head == NULL)
        return;
    if (*head == *tail) {
        free(*head);
        *head = *tail = NULL;
        return;
    }
    struct Node *t = *tail;
    *tail = t->prev;
    (*tail)->next = NULL;
    free(t);
}

void delete_at(struct Node **head, struct Node **tail, int pos)
{
    if (*head == NULL)
        return;
    int n = count_nodes(*head);
    if (pos < 0 || pos >= n)
        return;
    if (pos == 0) {
        delete_begin(head, tail);
        return;
    }
    if (pos == n - 1) {
        delete_end(head, tail);
        return;
    }
    struct Node *cur = *head;
    for (int i = 0; i < pos - 1; i++)
        cur = cur->next;
    struct Node *rm = cur->next;
    cur->next = rm->next;
    rm->next->prev = cur;
    free(rm);
}

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

int main(void)
{
    struct Node *head = NULL;
    struct Node *tail = NULL;

    insert_end(&head, &tail, 10);
    insert_end(&head, &tail, 20);
    insert_end(&head, &tail, 30);

    print_list(head);   /* 10 20 30 */

    delete_at(&head, &tail, 1);   /* remove 20 */
    delete_at(&head, &tail, 1);   /* remove 30 */
    delete_at(&head, &tail, 0);   /* remove 10 */

    print_list(head);   /* empty */

    return 0;
}

Output

First print_list(head) after three insert_end calls:

10 20 30

After delete_at at indices 1, 1, and 0, the second print_list prints only a newline (empty list).

Step-by-step execution table

Step Operation Branch List state (forward) List state (backward) head tail
0 (after insert_end ×3) 10 → 20 → 30 30 → 20 → 10 Node 10 Node 30
1 delete_at(1) Middle (unlink node 20) 10 → 30 30 → 10 Node 10 Node 30
2 delete_at(1) pos == n-1delete_end 10 10 Node 10 Node 10
3 delete_at(0) pos == 0delete_begin empty empty NULL NULL

Memory layout table (initial list 10 20 30)

Address Node data prev next Points to (prev / next)
0x1000 Node A 10 NULL 0x1008 prev: NULL, next: Node B
0x1008 Node B 20 0x1000 0x1010 prev: Node A, next: Node C
0x1010 Node C 30 0x1008 NULL prev: Node B, next: NULL

After first delete_at(1) (middle remove)

Node B is freed; A and C link to each other:

Address Node data prev next
0x1000 Node A 10 NULL 0x1010
0x1010 Node C 30 0x1000 NULL

Pointer variables

  • Initial list: head = 0x1000 (Node A), tail = 0x1010 (Node C).
  • After first delete: same head/tail addresses; list is 10 → 30.
  • After all deletes: head = tail = NULL.

Traversal path for print_list(head) (before any delete)

Start at head (0x1000) → Node A [10]
    ↓ (next)
Node B [20]
    ↓ (next)
Node C [30]
    ↓ (next)
NULL → Stop

Output: 10 20 30

Comparison: delete_at vs delete_begin / delete_end

Feature delete_at Direct delete_begin / delete_end
API One function + index pos Call the helper for front vs back only
This main sequence Middle unlink, then delete_end, then delete_begin Would match explicit calls in that order
Middle delete Walk + cur->next / rm->next->prev Not used when only deleting ends

Key branches inside delete_at

Condition Delegates to
pos == 0 delete_begin
pos == n - 1 delete_end
Otherwise (middle) Unlink cur->next, fix prev on the node after the removed one

Delete from Position (SLL) — same index idea, fewer pointer fields. · Delete from Beginning (DLL) · Delete from End (DLL)