Opening the reading…
Opening the reading…
LINKED LIST › LINKED LIST (PART 1)
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →A singly linked list does not reveal its length or let you jump to an index, so counting first and then walking back is possible but requires two passes. Instead, make one pointer move one node at a time and another move two nodes at a time. When the faster pointer has covered the list, the slower pointer has covered half of it.
The stopping point also handles both list lengths correctly. For an odd-length list, fast reaches the last node and slow sits on the exact middle. For an even-length list, fast moves past the last node and slow has already advanced to the second of the two middle nodes. Returning slow therefore matches the required tie-breaking rule without a separate case.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | The fast pointer advances two links per iteration, so the loop runs about n / 2 times; each iteration performs a constant number of link reads and assignments, and no node is revisited by a separate pass. |
| Space | O(1) extra | Only slow and fast are added beyond the input list, so the working memory stays constant even in the worst shape: a completely one-sided chain. The returned node is part of the input list, so no output storage is counted. |
#include <cstddef>
using namespace std;
struct ListNode {
int val;
ListNode* next;
ListNode() : val(0), next(nullptr) {}
ListNode(int value) : val(value), next(nullptr) {}
ListNode(int value, ListNode* nextNode) : val(value), next(nextNode) {}
};
class Solution {
public:
ListNode* middleNode(ListNode* head) {
ListNode* slow = head;
ListNode* fast = head;
while (fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
};The condition checks fast->next before the body moves fast by two links. That guard is both a safety check and the reason the even-length case chooses the second middle: when fast passes the final node, slow has just taken the extra step needed to move from the first middle to the second.
You can first count n by walking from head to null, then walk n / 2 steps from head and return that node. This is not an optimization: it still takes O(n) time and O(1) extra space, while the fast pointer solution combines both passes into one traversal. It remains a reasonable alternative when the midpoint index is needed for another operation or when the direct speed argument is less familiar.
#include <cstddef>
using namespace std;
struct ListNode {
int val;
ListNode* next;
ListNode() : val(0), next(nullptr) {}
ListNode(int value) : val(value), next(nullptr) {}
ListNode(int value, ListNode* nextNode) : val(value), next(nextNode) {}
};
class Solution {
public:
ListNode* middleNode(ListNode* head) {
int length = 0;
ListNode* current = head;
while (current != nullptr) {
++length;
current = current->next;
}
current = head;
for (int steps = 0; steps < length / 2; ++steps) {
current = current->next;
}
return current;
}
};