Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions problems/2095-delete-the-middle-node-of-a-linked-list/analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# 2095. Delete the Middle Node of a Linked List

[LeetCode Link](https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/)

Difficulty: Medium
Topics: Linked List, Two Pointers
Acceptance Rate: 60.0%

## Hints

### Hint 1

To delete the middle node you first need to *find* it. The middle is the `⌊n / 2⌋th` node (0-based). The naive idea is to walk the whole list once to learn `n`, then walk again to reach the middle. That works — but think about whether there is a way to locate the middle in a **single pass** without counting first. The "Two Pointers" tag is a strong nudge here.

### Hint 2

Consider two pointers that move through the list at different speeds: a `slow` pointer advancing one node per step and a `fast` pointer advancing two nodes per step. When `fast` reaches the end, where does `slow` end up? This "tortoise and hare" technique lets the two pointers naturally meet at the middle.

### Hint 3

Deleting a node in a singly linked list means rewiring its **predecessor** to skip over it (`prev.Next = node.Next`). So you don't just want a pointer *at* the middle — you want a pointer *just before* it. The classic trick is to keep `slow` one step behind by starting `fast` slightly ahead (e.g. advance `fast` by two before the loop, or track a separate `prev`). Don't forget the single-node case: deleting the only node should return an empty list (`nil`).

## Approach

We use the **fast/slow two-pointer** pattern to find the node *just before* the middle in one pass, then splice the middle node out.

Setup:
- If the list has 0 or 1 node, the middle is the head itself; deleting it yields an empty list, so return `nil` immediately.
- Otherwise initialize `slow = head`, `fast = head`, and a `prev` pointer that trails `slow`.

Traversal:
- Move `fast` forward by two nodes and `slow` forward by one node per iteration, updating `prev` to follow `slow`.
- Loop while `fast != nil && fast.Next != nil`.

When the loop ends, `slow` sits exactly on the middle node (`⌊n / 2⌋`) and `prev` is the node immediately before it. Why? The fast pointer covers twice the ground, so by the time it falls off the end, the slow pointer has covered half the list — landing on the floor-division midpoint for both odd and even lengths.

Splice:
- Set `prev.Next = slow.Next`, which unlinks the middle node.
- Return `head` (unchanged, since we never delete the head in this branch).

Example (`[1,3,4,7,1,2,6]`, n = 7, target index 3 = value 7):
- After the traversal `slow` lands on the node with value `7` and `prev` on `4`.
- `prev.Next = slow.Next` connects `4 -> 1`, producing `[1,3,4,1,2,6]`. ✓

Example (`[1,2,3,4]`, n = 4, target index 2 = value 3):
- `slow` lands on `3`, `prev` on `2`, giving `2 -> 4` and result `[1,2,4]`. ✓

This is a clean, single-pass solution and a great drill for mastering pointer rewiring.

## Complexity Analysis

Time Complexity: O(n) — one traversal where `fast` covers the list at double speed.
Space Complexity: O(1) — only a constant number of pointers are used.

## Edge Cases

- **Single node `[x]`**: The only node is the middle. Deleting it must return `nil` (empty list). Handled by the early return.
- **Two nodes `[a,b]`**: n = 2, middle index = 1, so the second node is removed, leaving `[a]`. Verifies that `prev` correctly trails and the head is preserved.
- **Odd vs. even length**: Floor division means the pointer math must land on `⌊n/2⌋` for both parities; the fast/slow setup handles both without special casing.
- **Empty list `nil`**: Although the constraints guarantee at least one node, defensively returning `nil` for a `nil` head keeps the function robust.
72 changes: 72 additions & 0 deletions problems/2095-delete-the-middle-node-of-a-linked-list/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
number: "2095"
frontend_id: "2095"
title: "Delete the Middle Node of a Linked List"
slug: "delete-the-middle-node-of-a-linked-list"
difficulty: "Medium"
topics:
- "Linked List"
- "Two Pointers"
acceptance_rate: 5996.8
is_premium: false
created_at: "2026-06-15T05:34:48.896774+00:00"
fetched_at: "2026-06-15T05:34:48.896774+00:00"
link: "https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/"
date: "2026-06-15"
---

# 2095. Delete the Middle Node of a Linked List

You are given the `head` of a linked list. **Delete** the **middle node** , and return _the_ `head` _of the modified linked list_.

The **middle node** of a linked list of size `n` is the `⌊n / 2⌋th` node from the **start** using **0-based indexing** , where `⌊x⌋` denotes the largest integer less than or equal to `x`.

* For `n` = `1`, `2`, `3`, `4`, and `5`, the middle nodes are `0`, `1`, `1`, `2`, and `2`, respectively.





**Example 1:**

![](https://assets.leetcode.com/uploads/2021/11/16/eg1drawio.png)


**Input:** head = [1,3,4,7,1,2,6]
**Output:** [1,3,4,1,2,6]
**Explanation:**
The above figure represents the given linked list. The indices of the nodes are written below.
Since n = 7, node 3 with value 7 is the middle node, which is marked in red.
We return the new list after removing this node.


**Example 2:**

![](https://assets.leetcode.com/uploads/2021/11/16/eg2drawio.png)


**Input:** head = [1,2,3,4]
**Output:** [1,2,4]
**Explanation:**
The above figure represents the given linked list.
For n = 4, node 2 with value 3 is the middle node, which is marked in red.


**Example 3:**

![](https://assets.leetcode.com/uploads/2021/11/16/eg3drawio.png)


**Input:** head = [2,1]
**Output:** [2]
**Explanation:**
The above figure represents the given linked list.
For n = 2, node 1 with value 1 is the middle node, which is marked in red.
Node 0 with value 2 is the only node remaining after removing node 1.



**Constraints:**

* The number of nodes in the list is in the range `[1, 105]`.
* `1 <= Node.val <= 105`
35 changes: 35 additions & 0 deletions problems/2095-delete-the-middle-node-of-a-linked-list/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

// 2095. Delete the Middle Node of a Linked List
//
// Approach: fast/slow two pointers (tortoise and hare).
// The fast pointer moves two nodes per step while slow moves one, so when fast
// reaches the end, slow lands on the middle node (floor(n/2), 0-based). We keep
// a `prev` pointer trailing slow so we can splice the middle node out in O(1)
// once it is found. Single pass, constant extra space.

// ListNode is a node in a singly linked list.
type ListNode struct {
Val int
Next *ListNode
}

func deleteMiddle(head *ListNode) *ListNode {
// 0 or 1 node: the middle is the head itself; deleting it empties the list.
if head == nil || head.Next == nil {
return nil
}

var prev *ListNode
slow, fast := head, head

for fast != nil && fast.Next != nil {
prev = slow
slow = slow.Next
fast = fast.Next.Next
}

// prev is the node just before the middle; skip over the middle node.
prev.Next = slow.Next
return head
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"reflect"
"testing"
)

// buildList constructs a linked list from a slice and returns its head.
func buildList(vals []int) *ListNode {
dummy := &ListNode{}
cur := dummy
for _, v := range vals {
cur.Next = &ListNode{Val: v}
cur = cur.Next
}
return dummy.Next
}

// toSlice converts a linked list back into a slice for comparison.
func toSlice(head *ListNode) []int {
var out []int
for node := head; node != nil; node = node.Next {
out = append(out, node.Val)
}
return out
}

func TestSolution(t *testing.T) {
tests := []struct {
name string
input []int
expected []int
}{
{"example 1: odd length, removes index 3", []int{1, 3, 4, 7, 1, 2, 6}, []int{1, 3, 4, 1, 2, 6}},
{"example 2: even length, removes index 2", []int{1, 2, 3, 4}, []int{1, 2, 4}},
{"example 3: two nodes, removes second", []int{2, 1}, []int{2}},
{"edge case: single node becomes empty", []int{42}, nil},
{"edge case: three nodes removes middle", []int{10, 20, 30}, []int{10, 30}},
{"edge case: five nodes removes index 2", []int{5, 4, 3, 2, 1}, []int{5, 4, 2, 1}},
{"edge case: empty input", nil, nil},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := toSlice(deleteMiddle(buildList(tt.input)))
if !reflect.DeepEqual(got, tt.expected) {
t.Errorf("deleteMiddle(%v) = %v, want %v", tt.input, got, tt.expected)
}
})
}
}