..

382. Linked List Random Node

Problem Description

Given a singly linked list, return a random node’s value from the linked list. Each node must have the same probability of being chosen.

Implement the Solution class:

  • Solution(ListNode head) Initializes the object with the integer array nums.
  • int getRandom() Chooses a node randomly from the list and returns its value. All the nodes of the list should be equally likely to be choosen.

Example 1:

Input
["Solution", "getRandom", "getRandom", "getRandom", "getRandom", "getRandom"]
[[[1, 2, 3]], [], [], [], [], []]
Output
[null, 1, 3, 2, 2, 3]

Explanation
Solution solution = new Solution([1, 2, 3]);
solution.getRandom(); // return 1
solution.getRandom(); // return 3
solution.getRandom(); // return 2
solution.getRandom(); // return 2
solution.getRandom(); // return 3
// getRandom() should return either 1, 2, or 3 randomly. Each element should have equal probability of returning.

Constraints:

  • The number of nodes in the linked list will be in the range [1, 104].
  • -10^4 <= Node.val <= 10^4
  • At most 104 calls will be made to getRandom().

Follow up:

  • What if the linked list is extremely large and its length is unknown to you?
  • Could you solve this efficiently without using extra space?

Solution

Naive approach is to traverse the list, get the length, and use random.randint(0, length) to select value. But the restriction is that “the linked list is extremely large and its length is unknown”

Core algorithm is Reservoir sampling

A naive way to explain the algorithm is as follows

the frist element can always be selected, using array represents the linked list visually

[a] <- a is the first element

res = a, 100%

when we move the pointer right, we will get:

[a, b], then there is 50% chance I get a, and 50% chance I get b, which is

res = 50% res, 50% b => 50% a, 50% b

when we have [a, b, c]

res = 2/3 res, 1/3 c => 1/3 a, 1/3 b, 1/3 c

etc..

Python

# @lc code=start
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:

    def __init__(self, head: Optional[ListNode]):
        self.head = head

    def getRandom(self) -> int:
        count = res = 0
        curr = self.head
        while curr:
            count += 1
            if random.random() <= 1 / count:
                res = curr.val 
            curr = curr.next

        return res


# Your Solution object will be instantiated and called as such:
# obj = Solution(head)
# param_1 = obj.getRandom()
# @lc code=end