给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。
实现 Solution 类:
Solution(ListNode head)使用整数数组初始化对象。int getRandom()从链表中随机选择一个节点并返回该节点的值。链表中所有节点被选中的概率相等。
示例:

输入["Solution", "getRandom", "getRandom", "getRandom", "getRandom", "getRandom"]
[[[1, 2, 3]], [], [], [], [], []]
输出
[null, 1, 3, 2, 2, 3]
解释
Solution solution = new Solution([1, 2, 3]);
solution.getRandom(); // 返回 1
solution.getRandom(); // 返回 3
solution.getRandom(); // 返回 2
solution.getRandom(); // 返回 2
solution.getRandom(); // 返回 3
// getRandom() 方法应随机返回 1、2、3中的一个,每个元素被返回的概率相等Code language: C++ (cpp)
提示:
- 链表中的节点数在范围
[1, 104]内 -104 <= Node.val <= 104- 至多调用
getRandom方法104次
进阶:
- 如果链表非常大且长度未知,该怎么处理?
- 你能否在不使用额外空间的情况下解决此问题?
思路:
要随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。首先想到用随机数访问数组下标即可, 于是直接把整个链表转换为数组:
Solution(ListNode* head) {
while (head) {
arr.push_back(head->val);
head = head->next;
}
}Code language: C++ (cpp)
再用 arr[rand() % arr.size()]; 去返回节点值就行了。但如果链表非常大且长度未知,还不能使用额外空间的情况下该怎么做呢?那就需要考虑如何当遍历完 n 个节点后,每个节点最终被选中的概率都是 \(1/n\)。也即是说,对于第 i 个节点,最终被保留的概率 = 第 i 步选中它的概率 × 之后所有步骤它都不被替换掉的概率,即:
$$P(\text{第i个节点被选中}) = \frac{1}{i} \times \left(1-\frac{1}{i+1}\right) \times \left(1-\frac{1}{i+2}\right) \times \cdots \times \left(1-\frac{1}{n}\right)$$
$$= \frac{1}{i} \times \frac{i}{i+1} \times \frac{i+1}{i+2} \times \cdots \times \frac{n-1}{n} = \frac{1}{n}$$
这便是被称为蓄水池抽样(Reservoir Sampling)的算法。
AC代码:
首先是用全部转为数组再进行抽样的做法:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
private:
std::vector<int> values;
public:
Solution(ListNode* head) {
ListNode* current = head;
while (current != nullptr) {
values.push_back(current->val);
current = current->next;
}
}
int getRandom() {
int randomIndex = rand() % values.size();
return values[randomIndex];
}
};
/**
* Your Solution object will be instantiated and called as such:
* Solution* obj = new Solution(head);
* int param_1 = obj->getRandom();
*/Code language: C++ (cpp)
然后是蓄水池抽样的做法:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
private:
ListNode* head;
public:
Solution(ListNode* head) {
this->head = head;
}
int getRandom() {
int result = head->val;
ListNode* current = head->next;
int count = 2;
while (current != nullptr) {
if (rand() % count == 0) {
result = current->val;
}
current = current->next;
count++;
}
return result;
}
};
/**
* Your Solution object will be instantiated and called as such:
* Solution* obj = new Solution(head);
* int param_1 = obj->getRandom();
*/Code language: C++ (cpp)
参考阅读:
