📌  相关文章
📜  从单链列表中选择一个随机节点(1)

📅  最后修改于: 2023-12-03 15:36:16.665000             🧑  作者: Mango

从单链列表中选择一个随机节点

随机选择一个单链列表中的节点可以有多种实现方式,下面将介绍其中两种常见的方法。

方法一:使用数组存储链表节点

第一种方法是将链表节点存储在数组中,然后随机生成一个数组下标,得到对应的链表节点。具体实现步骤如下:

  1. 遍历链表,将每一个节点存储在数组中。
  2. 生成一个随机数,表示要选择的节点在数组中的下标。
  3. 获取数组中对应下标的节点,并返回。

以下是实现代码:

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$ 的样本。具体实现步骤如下:

  1. 遍历链表,维护一个计数器 $count$,表示已经遍历的节点数。
  2. 对于第 $i$ 个节点,以 $\frac{1}{i}$ 的概率将其作为结果。
  3. 继续遍历链表,根据概率替换已有的结果,使最终结果随机化。

以下是实现代码:

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)$。与方法一相比,该方法不需要额外的数组空间,适用于链表较大的情况。