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 random answer from a list of one node is forced: keep that node. When the second node arrives, choose between the old candidate and the new node with equal probability. After the third node arrives, the new node should be kept with probability 1/3, while each earlier candidate should survive with probability 2/3. If an earlier node had probability 1/2 before this step, its final probability becomes 1/2 x 2/3 = 1/3.
The same argument continues at every position. When you reach node i, replace the candidate with probability 1/i; otherwise keep it. Every node has probability 1/i of becoming the candidate, and every candidate already in the reservoir has probability (i - 1)/i of surviving. Their probabilities are therefore equal, so after the final node every list node is represented with probability 1/n.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | The pointer advances once from the head to the end, and each node performs only constant-time random generation and assignment. No node is revisited, so the total work is proportional to the number of nodes. |
| Space | O(1) extra | Only the current pointer, the candidate, the position, and one random value are stored. The returned integer is not additional output storage, and the list itself is input rather than working memory. |
#include <cstdlib>
using namespace std;
class Solution {
public:
int getRandom(ListNode* head) {
int result = head->val;
ListNode* current = head->next;
int position = 2;
while (current != nullptr) {
int randomValue = rand() % position;
if (randomValue == 0) {
result = current->val;
}
current = current->next;
position++;
}
return result;
}
};The initial candidate handles the first node without needing a random choice. At the second node, position is 2, so randomValue == 0 occurs with probability 1/2. After processing any node, position is incremented before the next iteration, keeping the denominator aligned with the next node's 1-based position. Moving that increment earlier would make every replacement probability use the wrong position.
You can first count the nodes, choose a random index in the range 0 to n - 1, and then walk to that index. This is not a space optimisation: it also uses O(1) extra space and O(n) time, but it makes the uniform choice feel more direct. The cost is two traversals instead of one, while reservoir sampling works without ever knowing the length.
#include <cstdlib>
using namespace std;
class Solution {
public:
int getRandom(ListNode* head) {
int length = 0;
ListNode* current = head;
while (current != nullptr) {
length++;
current = current->next;
}
int target = rand() % length;
current = head;
while (target > 0) {
current = current->next;
target--;
}
return current->val;
}
};