代码改变世界

[LeetCode] 382. Linked List Random Node_Medium tag: linked list, math

2021-08-23 04:36  Johnson_强生仔仔  阅读(28)  评论(0编辑  收藏  举报

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].
  • -104 <= Node.val <= 104
  • At most 104 calls will be made to getRandom.

 

Ideas: 

1. T: O(n)   S: O(n)  直接将list 转换为array,然后利用random.random()* len(arr)得到random 的index, 最后返回相应的数值即可

random.random()   => random float in range [0, 1]

random.randint(start, end)  both include

Code

# 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]):
        """
        @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node.
        """
        self.range = []
        self.n = 0
        while head:
            self.range.append(head.val)
            self.n += 1
            head = head.next
        

    def getRandom(self) -> int:
        """
        Returns a random node's value.
        """
        pick = int(random.random() * self.n)
        return self.range[pick]

 

2. Follow up , what if the list is very big and its length is unknown?

Use "reservoir sampling" (随机抽样,并且概率相同)

1. 如果list 很大, 那么我们先从前i个里面选一个概率为1/ i

2. 然后再换这个node的概率为1/ (i + 1),也就是说保持这个node的概率为 ( 1 - 1/ (i + 1)) = i/(i + 1)

3. 然后每次index + 1, 这样的话概率为 1/ (i + 2), 保持这个node的概率为 ( 1 - 1/ (i + 2)) = i + 1/(i + 2)

4. 在n 次之后, 就是1/ i *   (i / i + 1)  * (i + 1/ i + 2) .... n - 1/ n = 1/ n

5. 也就是说每个node最后保持到最后的概率为1/ n

 

 

Code

# 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]):
        """
        @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node.
        """
        self.head = head
        

    def getRandom(self) -> int:
        """
        Returns a random node's value.
        """
        result, node, index = self.head, self.head.next, 1
        while node:
            if random.randint(0, index) == 0:
                result = node
            node = node.next
            index += 1
        return result.val