📅  最后修改于: 2023-12-03 15:36:16.665000             🧑  作者: Mango
随机选择一个单链列表中的节点可以有多种实现方式,下面将介绍其中两种常见的方法。
第一种方法是将链表节点存储在数组中,然后随机生成一个数组下标,得到对应的链表节点。具体实现步骤如下:
以下是实现代码:
public ListNode getRandomNode(ListNode head) {
// 定义一个数组,存储链表节点
List<ListNode> list = new ArrayList<>();
// 将链表中每一个节点存储到数组中
while (head != null) {
list.add(head);
head = head.next;
}
// 生成一个随机下标
int randomIndex = new Random().nextInt(list.size());
// 返回数组中对应下标的节点
return list.get(randomIndex);
}
该方法的时间复杂度为 $O(n)$,空间复杂度为 $O(n)$。由于需要额外的数组空间存储链表节点,所以不适用于链表较大的情况。
第二种方法是使用蓄水池抽样算法(Reservoir Sampling Algorithm)。该算法可以在不知道样本总量的情况下,从数据流中随机选择 $k$ 个样本。在单链列表中选择一个随机节点,相当于选择一个 $k=1$ 的样本。具体实现步骤如下:
以下是实现代码:
public ListNode getRandomNode(ListNode head) {
// 计数器,表示已经遍历的节点数
int count = 0;
// 最终结果
ListNode result = head;
// 遍历链表
while (head != null) {
count++;
// 以 1/i 的概率替换结果
if (new Random().nextInt(count) == 0) {
result = head;
}
// 继续遍历链表
head = head.next;
}
// 返回结果
return result;
}
该方法的时间复杂度为 $O(n)$,空间复杂度为 $O(1)$。与方法一相比,该方法不需要额外的数组空间,适用于链表较大的情况。