题目:
给你一个未知长度的链表,请你设计一个算法,只能遍历一次,随机地返回链表中的一个节点。
LeetCode 382 和 398 题,水塘抽样算法(Reservoir Sampling),本质上是一种随机概率算法。
382题 链表随机节点:
382. 链表随机节点
给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。
进阶:
如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?
示例:
// 初始化一个单链表 [1,2,3].
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
Solution solution = new Solution(head);
// getRandom()方法应随机返回1,2,3中的一个,保证每个元素被返回的概率相等。
solution.getRandom();
一遍遍历,这个很重要。
code:
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def __init__(self, head):
self.head = head
def getRandom(self):
"""
Returns a random node's value.
:rtype: int
"""
self.res = self.head
i = 0
ret = 0
while self.res:
a = random.randint(0,i)
if a==0:
ret = self.res.val
i += 1
self.res = self.res.next
return ret
# Your Solution object will be instantiated and called as such:
# obj = Solution(head)
# param_1 = obj.getRandom()
证明:
参考链接
这个证明,真的是这个题的核心。主要思想就是,有多少概率选则当前值,如果选了,有多少概率不被替换。最后发现选了+不被替换的概率刚好是1/n。
证明要多想想,挺不错的。
接下来看一个几乎一样的题。
leetcode 398题 随机数索引:
398. 随机数索引
给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。
注意:
数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。
示例:
int[] nums = new int[] {1,2,3,3,3};
Solution solution = new Solution(nums);
// pick(3) 应该返回索引 2,3 或者 4。每个索引的返回概率应该相等。
solution.pick(3);
// pick(1) 应该返回 0。因为只有nums[0]等于1。
solution.pick(1);
code:
class Solution(object):
def __init__(self, nums):
"""
:type nums: List[int]
"""
self.nums = nums
def pick(self, target):
"""
:type target: int
:rtype: int
"""
ret = -1
i = 0
for r, num in enumerate(self.nums):
if target==num:
a = random.randint(0,i)
if a==0:
ret = r
i += 1
return ret
# Your Solution object will be instantiated and called as such:
# obj = Solution(nums)
# param_1 = obj.pick(target)
好了,这个题挺有意思。