import random
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def __init__(self, head: ListNode):
self.head = head
def getRandom(self) -> int:
# Reservoir Sampling
result = self.head.val
node = self.head.next
i = 2
while node:
if random.randrange(i) == 0:
result = node.val
node = node.next
i += 1
return result
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
#include <cstdlib>
class Solution {
public:
ListNode* head;
Solution(ListNode* head) {
this->head = head;
}
int getRandom() {
// Reservoir Sampling
int result = head->val;
ListNode* node = head->next;
int i = 2;
while (node) {
if (rand() % i == 0) {
result = node->val;
}
node = node->next;
i++;
}
return result;
}
};
import java.util.Random;
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
private ListNode head;
private Random rand;
public Solution(ListNode head) {
this.head = head;
this.rand = new Random();
}
public int getRandom() {
int result = head.val;
ListNode node = head.next;
int i = 2;
while (node != null) {
if (rand.nextInt(i) == 0) {
result = node.val;
}
node = node.next;
i++;
}
return result;
}
}
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
var Solution = function(head) {
this.head = head;
};
/**
* @return {number}
*/
Solution.prototype.getRandom = function() {
let result = this.head.val;
let node = this.head.next;
let i = 2;
while (node) {
if (Math.floor(Math.random() * i) === 0) {
result = node.val;
}
node = node.next;
i++;
}
return result;
};
You are given a singly linked list whose head node is head
. You need to implement a class Solution
with two methods:
Solution(ListNode head)
- Initializes the object with the head of the linked list.getRandom()
- Returns a random node's value from the linked list. Each node must have an equal probability of being chosen.Key constraints:
getRandom()
must select any node uniformly at random.
At first glance, it seems straightforward to select a random node from a linked list if you know its length. If you know the length n
, you could generate a random index between 0 and n-1
and traverse the list to that index. However, the problem does not guarantee that you know the length in advance, and it's inefficient to traverse the list twice (once to count, once to select) for each getRandom()
call.
A brute-force approach would be to store all node values in an array during initialization, then pick a random index from the array. However, this uses extra space proportional to the list length, which may not be acceptable for very large lists.
To optimize, we need a way to select a random node in a single pass and with constant space. This leads to a classic algorithm called reservoir sampling, which allows us to sample uniformly from a stream (or linked list) when we don't know its length in advance.
We use the reservoir sampling algorithm to select a random node from the linked list with equal probability, using only constant extra space.
getRandom()
:
result
be the value of the head node.i
(starting from 2 for the second node).i-1
. If the random integer is 0, update result
to the current node's value.result
.result
with the current node's value is 1/i
. This ensures that after traversing the entire list, each node has an equal probability of being chosen.
This method requires only a single pass through the list per getRandom()
call, and uses only constant extra space.
Suppose the linked list is: 1 → 2 → 3
result = 1
result = 2
; otherwise, keep result = 1
.result = 3
; otherwise, keep the previous result
.result
. Each node has a 1/3 chance of being picked.
Over many calls to getRandom()
, the probability of returning 1, 2, or 3 will be approximately equal.
getRandom()
.
getRandom()
call (must traverse the list each time).
The optimized approach is preferred when the list can be very large or when memory usage is critical.
The Linked List Random Node problem is elegantly solved using reservoir sampling, which allows for uniform random selection from a list without needing to know its length or store all values. This approach is efficient in space and simple to implement, relying on probability to ensure fairness. The key insight is that at each node, you update your choice with decreasing probability, guaranteeing that each node is chosen with equal likelihood in a single traversal.