Topics › Linked List › Medium Problems of LL
Find Middle of Linked List
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given the head of a singly linked list. Return the middle node of the list. When the list has an even number of nodes there are two middle nodes, and you must return the second of them. The answer is the node itself, not just its value, so the caller can keep walking the list from that point.
Constraints: 1 ≤ n ≤ 100 in the LeetCode version, node values fit in an int. The Java solutions also accept an empty list and return null for it.
static class ListNode {
int val;
ListNode next;
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
Example 1
head = 1 -> 2 -> 3 -> 4 -> 5
answer = the node holding 3 // two nodes on each side of it
Example 2
head = 1 -> 2 -> 3 -> 4 -> 5 -> 6
answer = the node holding 4 // the middles are 3 and 4; the second one is asked for
A useful way to pin down “the middle” precisely: with 0-based positions, the requested node is at index n / 2 (integer division). For n = 5 that is index 2, the third node. For n = 6 it is index 3, the fourth node. Both approaches below are just different ways of reaching index n / 2.
Approach 1: Brute force, count then walk
Intuition. With an array you would simply return a[n / 2]. A linked list gives you neither its length nor random access, so you have to earn both by walking. The most direct plan is therefore two passes: the first pass counts the nodes, the second pass starts again at the head and takes exactly n / 2 steps. Nothing about this is clever, and it is already linear time, which is the best possible because you cannot find the middle without at least reaching the end.
Algorithm.
- Walk the whole list once and count the nodes into
len. - Start a pointer
midat the head. - Move
midforwardlen / 2times. - Return
mid.
static ListNode bruteForce(ListNode head) {
int len = 0;
for (ListNode t = head; t != null; t = t.next) len++;
ListNode mid = head;
for (int i = 0; i < len / 2; i++) mid = mid.next; // index len / 2 is the (second) middle
return mid;
}
Complexity. Time O(n): n steps to count plus n / 2 steps to walk back to the middle, about 1.5n node visits in total. Space O(1): one counter and one pointer.
A variation stores every node reference in an ArrayList during a single pass and then returns list.get(n / 2). That avoids the second walk but costs O(n) extra memory, which is a poor trade for a problem that can be solved in constant space.
So why look further? The count-then-walk method needs the whole list to be traversed before it can even start looking for the answer. The next approach finds the middle during a single traversal, and the pattern it uses (two pointers moving at different speeds) shows up again in cycle detection, palindrome checks, list merge sort and reordering problems. It is worth learning here, where it is easiest to reason about.
Approach 2: Optimal, tortoise and hare
Intuition. Put two pointers at the head. Each round, slow moves one node and fast moves two nodes. After k rounds, slow is at index k and fast is at index 2k. The moment fast cannot go any further it has covered the whole list, so 2k is about n, which means slow is at about n / 2. The ratio of the two speeds performs the division by two for you, and you never need to know n explicitly.
The exact loop condition decides which middle you get, so it is worth checking both parities carefully. The loop keeps going while fast != null && fast.next != null, that is, while fast can legally take two more steps.
- Odd length
n = 2m + 1: aftermroundsfastis at index2m, the last node. Itsnextisnull, so the loop stops.slowis at indexm = n / 2, the unique middle. - Even length
n = 2m: afterm - 1roundsfastis at index2m - 2, andfast.next(index2m - 1) exists, so one more round runs. Nowfastis at index2m, which is one past the end, sofastisnulland the loop stops.slowis at indexm = n / 2, the second middle.
In both cases slow lands on index n / 2, exactly the node the brute force reaches.
If a problem asks for the first middle of an even list instead (which is what you want when splitting a list into two halves, as in merge sort, where cutting a two-node list after its second middle would never make it smaller), change the condition to fast.next != null && fast.next.next != null. Then slow stops one node earlier on even lengths and stays the same on odd lengths.
Algorithm.
- Set
slow = headandfast = head. - While
fast != nullandfast.next != null: moveslowone step andfasttwo steps. - Return
slow.
static ListNode optimal(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) { // fast can still take two steps
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
Dry run on 1 -> 2 -> 3 -> 4 -> 5 -> 6:
| round | slow | fast | can fast take two steps? |
|---|---|---|---|
| start | 1 | 1 | yes, 2 exists |
| 1 | 2 | 3 | yes, 4 exists |
| 2 | 3 | 5 | yes, 6 exists |
| 3 | 4 | null | no, stop |
The method returns the node holding 4, the second middle. On 1 -> 2 -> 3 -> 4 -> 5 the rounds end with slow on 3 and fast on 5, and since 5 has no successor the loop stops there.
Complexity. Time O(n): the loop runs about n / 2 times and does constant work per round. Space O(1): two pointers.
Note that the total number of pointer hops is roughly the same as in the brute force, because fast still touches every other node and slow touches half of them. The gain is not raw speed; it is that the answer is found in one traversal, with no length variable, and the technique carries over to many harder problems.
Edge cases and pitfalls
- Empty list.
headisnull. Thefast != nullcheck stops the loop immediately andnullis returned. The brute force counts zero nodes, takes zero steps and also returnsnull. - Single node.
fast.nextisnull, so the loop never runs and the head is returned, which is correct: one node is its own middle. - Two nodes. The loop runs once and returns the second node. If you expected the first node, you are thinking of the first-middle variant; know which one your problem wants.
- Order of the null checks. Write
fast != null && fast.next != null, in that order. The&&short-circuits, sofast.nextis only read whenfastexists. Swapping them throws aNullPointerExceptionon even-length lists. - Return the node, not the value. With duplicate values such as
5 -> 5 -> 5 -> 5, returning “a node with value 5” is not enough. The Java tests check node identity, so an off-by-one would be caught even when the values are equal. - Do not modify the list. Neither method changes any
nextpointer, which matters when the caller goes on to use both halves.
Related problems
- Middle of a LinkedList, Tortoise-Hare method is the same problem in the A2Z sheet.
- Delete the middle node in LL needs the node just before the middle, a small twist on the same loop.
- Check if LL is palindrome or not finds the middle, reverses the second half and compares.
- Reorder List splits the list at the first middle before weaving the halves.
- Sort LL uses the middle to split the list for merge sort.
- Detect a loop in LL uses the same slow and fast pointers to detect a cycle.