Bird
Raised Fist0
DSA Cprogramming

Delete from End of Doubly Linked List in DSA C

Choose your learning style10 modes available
Mental Model
To remove the last item, find the tail and unlink it from the list, updating the previous node to be the new tail.
Analogy: Imagine a train of connected cars. To remove the last car, you detach it from the second last car, making the second last car the new end of the train.
head -> 1 ↔ 2 ↔ 3 ↔ 4 ↔ 5 ← tail
null ← 1 ↔ 2 ↔ 3 ↔ 4 ↔ 5 -> null
Dry Run Walkthrough
Input: list: 1 ↔ 2 ↔ 3 ↔ 4 ↔ 5, delete from end
Goal: Remove the last node (5) and update the list tail
Step 1: start at head and move forward to find last node
1 ↔ 2 ↔ 3 ↔ 4 ↔ 5 -> null
Why: We need to find the last node to remove it
Step 2: last node (5) found, update previous node's next to null
1 ↔ 2 ↔ 3 ↔ 4 -> null  5 (removed)
Why: Disconnect last node from the list
Step 3: update tail pointer to previous node (4)
1 ↔ 2 ↔ 3 ↔ 4 ← tail -> null
Why: New tail is the node before the removed node
Result:
1 ↔ 2 ↔ 3 ↔ 4 -> null
Annotated Code
DSA C
#include <stdio.h>
#include <stdlib.h>

// Node structure for doubly linked list
typedef struct Node {
    int data;
    struct Node* prev;
    struct Node* next;
} Node;

// Function to create a new node
Node* createNode(int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->prev = NULL;
    newNode->next = NULL;
    return newNode;
}

// Function to append node at end
void append(Node** head_ref, int data) {
    Node* newNode = createNode(data);
    if (*head_ref == NULL) {
        *head_ref = newNode;
        return;
    }
    Node* temp = *head_ref;
    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = newNode;
    newNode->prev = temp;
}

// Function to delete node from end
void deleteFromEnd(Node** head_ref) {
    if (*head_ref == NULL) {
        return; // empty list
    }
    Node* temp = *head_ref;
    // move to last node
    while (temp->next != NULL) {
        temp = temp->next; // advance to tail
    }
    if (temp->prev != NULL) {
        temp->prev->next = NULL; // unlink last node
    } else {
        *head_ref = NULL; // list had one node, now empty
    }
    free(temp); // free memory
}

// Function to print list forward
void printList(Node* head) {
    Node* temp = head;
    while (temp != NULL) {
        printf("%d", temp->data);
        if (temp->next != NULL) {
            printf(" ↔ ");
        }
        temp = temp->next;
    }
    printf(" -> null\n");
}

int main() {
    Node* head = NULL;
    append(&head, 1);
    append(&head, 2);
    append(&head, 3);
    append(&head, 4);
    append(&head, 5);

    deleteFromEnd(&head);

    printList(head);
    return 0;
}
while (temp->next != NULL) { temp = temp->next; }
advance temp to last node to find tail
if (temp->prev != NULL) { temp->prev->next = NULL; } else { *head_ref = NULL; }
unlink last node and update tail or empty list
free(temp);
free memory of removed node
OutputSuccess
1 ↔ 2 ↔ 3 ↔ 4 -> null
Complexity Analysis
Time: O(n) because we traverse the list from head to tail once to find the last node
Space: O(1) because we only use a few pointers and no extra data structures
vs Alternative: Compared to singly linked list, doubly linked list allows easier backward traversal but still requires O(n) to find tail if no tail pointer is maintained
Edge Cases
empty list
function returns immediately without changes
DSA C
if (*head_ref == NULL) { return; }
list with one node
node is removed and head is set to NULL
DSA C
else { *head_ref = NULL; }
When to Use This Pattern
When you need to remove the last element from a doubly linked list, look for a pattern that finds the tail and updates the previous node's next pointer to null.
Common Mistakes
Mistake: Not updating the previous node's next pointer to null, leaving a dangling pointer
Fix: Set temp->prev->next = NULL before freeing the last node
Mistake: Not handling the case when the list has only one node
Fix: Check if temp->prev is NULL and update head to NULL accordingly
Summary
Deletes the last node from a doubly linked list by unlinking it and updating pointers.
Use when you want to remove the tail element from a doubly linked list.
The key is to find the last node, unlink it from its previous node, and update the tail reference.

Practice

(1/5)
1. What happens to the tail pointer when you delete the last node from a doubly linked list?
easy
A. It becomes NULL immediately without checking.
B. It remains unchanged.
C. It points to the head node.
D. It moves to the previous node of the deleted node.

Solution

  1. Step 1: Understand the tail pointer role

    The tail pointer always points to the last node in the doubly linked list.
  2. Step 2: Update tail after deletion

    When the last node is deleted, the tail pointer must move to the previous node to keep pointing to the new last node.
  3. Final Answer:

    It moves to the previous node of the deleted node. -> Option D
  4. Quick Check:

    Tail points to previous node after deletion [OK]
Hint: Tail always points to last node; update to previous on delete [OK]
Common Mistakes:
  • Setting tail to NULL without checking if list is empty
  • Not updating tail pointer after deletion
  • Pointing tail to head incorrectly
2. Which of the following is the correct syntax to delete the last node in a doubly linked list in C?
easy
A. tail = tail->prev; free(tail->next); tail->next = NULL;
B. free(tail); tail = tail->prev; tail->next = NULL;
C. tail->prev = tail; free(tail); tail = NULL;
D. tail->next = NULL; free(tail); tail = tail->prev;

Solution

  1. Step 1: Free the last node correctly

    We first move tail to previous node, then free the old last node which is tail->next.
  2. Step 2: Update tail's next pointer

    After freeing, set tail->next to NULL to mark new end of list.
  3. Final Answer:

    tail = tail->prev; free(tail->next); tail->next = NULL; -> Option A
  4. Quick Check:

    Move tail first, free old node, update next pointer [OK]
Hint: Move tail first, then free old node, update next pointer [OK]
Common Mistakes:
  • Freeing tail before moving pointer
  • Not setting tail->next to NULL
  • Incorrect pointer assignments causing memory errors
3. What will be the output of the following code snippet after deleting the last node?
typedef struct Node {
  int data;
  struct Node* prev;
  struct Node* next;
} Node;

Node* tail = NULL;
// List: 10 <-> 20 <-> 30
// tail points to node with data 30

// Delete last node
Node* temp = tail;
tail = tail->prev;
free(temp);
tail->next = NULL;

// Print list from head
Node* head = tail;
while(head->prev) head = head->prev;
while(head) {
  printf("%d ", head->data);
  head = head->next;
}
medium
A. 10 20
B. 10 20 30
C. 20 30
D. 10 30

Solution

  1. Step 1: Understand list before deletion

    Initial list is 10 <-> 20 <-> 30, tail points to 30.
  2. Step 2: Delete last node and update pointers

    Last node 30 is freed, tail moves to 20, tail->next set to NULL.
  3. Step 3: Print list from head

    Head is found by moving to start (10), printing nodes 10 and 20.
  4. Final Answer:

    10 20 -> Option A
  5. Quick Check:

    Last node removed, list prints 10 20 [OK]
Hint: After deletion, tail moves back; print from head to see updated list [OK]
Common Mistakes:
  • Printing old tail node after deletion
  • Not updating tail->next to NULL
  • Starting print from tail instead of head
4. Identify the error in the following code snippet that deletes the last node of a doubly linked list:
void deleteEnd(Node** tail) {
  if (*tail == NULL) return;
  Node* temp = *tail;
  *tail = (*tail)->prev;
  free(temp);
  (*tail)->next = NULL;
}
medium
A. Not freeing the node properly.
B. Dereferencing tail without checking if list becomes empty.
C. Not updating the head pointer.
D. Using double pointer for tail unnecessarily.

Solution

  1. Step 1: Check for empty list after deletion

    If the list had only one node, after deletion tail becomes NULL, so (*tail)->next causes error.
  2. Step 2: Fix by checking tail before dereference

    Must check if *tail is not NULL before setting (*tail)->next = NULL.
  3. Final Answer:

    Dereferencing tail without checking if list becomes empty. -> Option B
  4. Quick Check:

    Check tail NULL before accessing next pointer [OK]
Hint: Always check if tail is NULL before accessing its members [OK]
Common Mistakes:
  • Dereferencing NULL pointer after deletion
  • Ignoring empty list edge case
  • Not freeing memory causing leaks
5. You have a doubly linked list with nodes: 5 <-> 15 <-> 25 <-> 35. After deleting the last node twice, what will be the printed list from head?
// Initial tail points to 35
// Delete last node twice
Node* temp;
temp = tail;
tail = tail->prev;
free(temp);
tail->next = NULL;

temp = tail;
tail = tail->prev;
free(temp);
tail->next = NULL;

// Print from head
Node* head = tail;
while(head->prev) head = head->prev;
while(head) {
  printf("%d ", head->data);
  head = head->next;
}
hard
A. 5 15 25
B. 15 25
C. 5 15
D. 25 35

Solution

  1. Step 1: Understand initial list and tail

    List is 5 <-> 15 <-> 25 <-> 35, tail points to 35.
  2. Step 2: Delete last node once

    Delete 35, tail moves to 25, tail->next = NULL.
  3. Step 3: Delete last node second time

    Delete 25, tail moves to 15, tail->next = NULL.
  4. Step 4: Print list from head

    Head found by moving to start (5), print nodes 5 and 15.
  5. Final Answer:

    5 15 -> Option C
  6. Quick Check:

    Two deletions remove last two nodes, list prints 5 15 [OK]
Hint: Each delete moves tail back; print from head to see current list [OK]
Common Mistakes:
  • Not updating tail->next to NULL after each deletion
  • Printing from tail instead of head
  • Assuming deleted nodes still exist
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard