Bird
Raised Fist0
DSA Typescriptprogramming

Delete from End of Doubly Linked List in DSA Typescript

Choose your learning style10 modes available
Mental Model
Access the tail directly and unlink it from the previous node using the prev pointer, then update tail to the previous node for O(1) removal.
Analogy: Imagine a train with carriages linked front and back. To remove the last carriage, detach it from the previous one using the backward link, and declare the previous as the new end.
head -> 1 ↔ 2 ↔ 3 ↔ 4 ↔ 5 <- tail
null <- 1 ↔ 2 ↔ 3 ↔ 4 ↔ 5 -> null
Dry Run Walkthrough
Input: list: head=1 ↔ 2 ↔ 3 ↔ 4 ↔ 5=tail, delete from end
Goal: Remove node 5, set 4.next=null, update tail=4
Step 1: if (!this.tail) return; // list not empty
head -> 1 ↔ 2 ↔ 3 ↔ 4 ↔ 5 <- tail
Why: Early return for empty list
Step 2: if (this.head === this.tail) { clear head/tail; return; } // single node
head -> 1 ↔ 2 ↔ 3 ↔ 4 ↔ 5 <- tail
Why: Handle single node case
Step 3: const prevNode = this.tail.prev; // prevNode = 4
head -> 1 ↔ 2 ↔ 3 ↔ 4 ↑ prevNode  ↔ 5 <- tail
Why: Use prev pointer for O(1) access to node before tail
Step 4: prevNode.next = null; // unlink 5
head -> 1 ↔ 2 ↔ 3 ↔ 4 -> null  (5 detached)
null <- 1 ↔ 2 ↔ 3 ↔ 4 -> null
Why: Sever the forward link from previous node to old tail
Step 5: this.tail = prevNode; // tail now points to 4
head -> 1 ↔ 2 ↔ 3 ↔ 4 -> null <- tail
Why: Update tail pointer to new last node
Result:
head -> 1 ↔ 2 ↔ 3 ↔ 4 -> null <- tail
Annotated Code
DSA Typescript
class Node {
  value: number;
  next: Node | null;
  prev: Node | null;

  constructor(value: number) {
    this.value = value;
    this.next = null;
    this.prev = null;
  }
}

class DoublyLinkedList {
  head: Node | null;
  tail: Node | null;

  constructor() {
    this.head = null;
    this.tail = null;
  }

  append(value: number): void {
    const newNode = new Node(value);
    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;
      return;
    }
    this.tail!.next = newNode;
    newNode.prev = this.tail;
    this.tail = newNode;
  }

  deleteFromEnd(): void {
    if (!this.tail) return;

    if (this.head === this.tail) {
      this.head = null;
      this.tail = null;
      return;
    }

    const prevNode = this.tail.prev!;
    prevNode.next = null;
    this.tail = prevNode;
  }

  printList(): void {
    let curr = this.head;
    let result = '';
    while (curr) {
      result += curr.value + (curr.next ? ' ↔ ' : ' -> null');
      curr = curr.next;
    }
    console.log(result);
  }
}

// Driver code
const dll = new DoublyLinkedList();
[1,2,3,4,5].forEach(v => dll.append(v));
dll.deleteFromEnd();
dll.printList();
if (!this.tail) return;
Guard for empty list
if (this.head === this.tail) { this.head = null; this.tail = null; return; }
Guard for single node: reset both pointers
const prevNode = this.tail.prev!;
O(1) access to second-last node via tail.prev
prevNode.next = null;
Unlink old tail forward
this.tail = prevNode;
Shift tail to second-last
OutputSuccess
1 ↔ 2 ↔ 3 ↔ 4 -> null
Complexity Analysis
Time: O(1) - direct tail access, no traversal needed
Space: O(1) - constant extra pointers
vs Alternative: Singly linked list delete-end is O(n) (traverse to second-last); DLL is O(1) thanks to tail.prev
Edge Cases
empty list
returns early, no change
DSA Typescript
if (!this.tail) return;
single node
head and tail set to null
DSA Typescript
if (this.head === this.tail) { ... }
When to Use This Pattern
Maintain head/tail pointers in DLL for O(1) insert/delete at both ends; use .prev/.next for unlinking without traversal.
Common Mistakes
Mistake: Traversing from head to find second-last (O(n)) instead of using tail.prev
Fix: Leverage tail.prev for direct access
Mistake: Forgetting to update tail pointer, leaving it dangling
Fix: Always set this.tail = prevNode after unlinking
Mistake: Not handling single-node or empty cases
Fix: Add explicit if-guards
Summary
O(1) delete from end in DLL: access tail.prev, unlink (prev.next=null), update tail=prevNode.
Key enablers: tail pointer + bidirectional links.
Edge cases: empty/single node require special handling.

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
B. It moves to the previous node of the deleted node
C. It points to the head node
D. It remains unchanged

Solution

  1. Step 1: Understand the tail pointer role

    The tail pointer always points to the last node in the list.
  2. Step 2: Effect of deleting the last node

    When the last node is deleted, the tail must move to the previous node to remain the last node.
  3. Final Answer:

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

    Tail updates to previous node after deletion [OK]
Hint: Tail always points to last node, so move it back after deletion [OK]
Common Mistakes:
  • Setting tail to null without checking if list is empty
  • Not updating tail pointer after deletion
  • Confusing head and tail pointers
  • Assuming tail points to head after deletion
2. Which of the following is the correct TypeScript syntax to delete the last node from a doubly linked list assuming tail is not null?
easy
A. tail = tail.prev; tail.next = null;
B. tail = tail.next; tail.prev = null;
C. tail = null; head = null;
D. tail.prev = tail; tail.next = null;

Solution

  1. Step 1: Move tail pointer to previous node

    To delete the last node, tail must point to its previous node: tail = tail.prev;
  2. Step 2: Set new tail's next to null

    Since tail is now last node, its next must be null: tail.next = null;
  3. Final Answer:

    tail = tail.prev; tail.next = null; -> Option A
  4. Quick Check:

    Correct syntax moves tail back and nullifies next [OK]
Hint: Tail moves back, then tail.next set to null [OK]
Common Mistakes:
  • Moving tail forward instead of backward
  • Not setting tail.next to null
  • Setting tail to null without updating links
  • Confusing prev and next pointers
3. Given the doubly linked list: 1 <-> 2 <-> 3 <-> 4 <-> null, what is the list after deleting the last node?
medium
A. 1 <-> 2 <-> 3 <-> null
B. 1 <-> 2 <-> 3 <-> 4 <-> null
C. 2 <-> 3 <-> 4 <-> null
D. 1 <-> 2 <-> null

Solution

  1. Step 1: Identify the last node

    The last node is 4, pointed by tail.
  2. Step 2: Delete last node and update tail

    Remove node 4, update tail to node 3, set tail.next to null.
  3. Final Answer:

    1 <-> 2 <-> 3 <-> null -> Option A
  4. Quick Check:

    Last node 4 removed, tail now 3 [OK]
Hint: Remove last node, tail moves to previous node [OK]
Common Mistakes:
  • Not updating tail pointer
  • Leaving deleted node linked
  • Removing wrong node
  • Confusing head and tail updates
4. Consider this TypeScript code snippet to delete the last node from a doubly linked list:
if (tail !== null) {
  tail = tail.prev;
  tail.next = null;
}
What error can occur here?
medium
A. No error, code is correct
B. Syntax error due to missing semicolon
C. Runtime error if list has only one node
D. Tail pointer moves forward incorrectly

Solution

  1. Step 1: Analyze single-node list case

    If list has one node, tail.prev is null, so tail becomes null.
  2. Step 2: Accessing tail.next causes error

    After tail = tail.prev (null), next line tries tail.next = null, causing runtime error (cannot access property of null).
  3. Final Answer:

    Runtime error if list has only one node -> Option C
  4. Quick Check:

    Null tail causes runtime error on next access [OK]
Hint: Check if tail.prev is null before setting tail.next [OK]
Common Mistakes:
  • Not handling single-node list edge case
  • Assuming tail is never null after update
  • Ignoring runtime null pointer errors
  • Confusing syntax errors with runtime errors
5. You have a doubly linked list with nodes: 10 <-> 20 <-> 30. After deleting the last node twice, what is the final list?
hard
A. 10 <-> 20 <-> null
B. 20 <-> null
C. null
D. 10 <-> null

Solution

  1. Step 1: Delete last node first time

    Last node 30 is removed, tail moves to 20, list becomes 10 <-> 20 <-> null.
  2. Step 2: Delete last node second time

    Last node 20 is removed, tail moves to 10, list becomes 10 <-> null.
  3. Step 3: Verify final list

    After two deletions, list is 10 <-> null.
  4. Final Answer:

    10 <-> null -> Option D
  5. Quick Check:

    Two deletions remove 30 and 20, leaving 10 [OK]
Hint: Delete last node twice removes last two nodes [OK]
Common Mistakes:
  • Deleting only one node instead of two
  • Confusing node values after deletion
  • Not updating tail pointer correctly
  • Assuming list becomes empty too early
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