382. Linked List Random Node

本文介绍了一种高效地从链表中获取随机节点的方法——水塘抽样法,并提供了详细的算法解释及实现代码。

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.

Follow up:
What if the linked list is extremely large and its length is unknown to you? Could you solve this efficiently without using extra space?

Example:

// Init a singly linked list [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() should return either 1, 2, or 3 randomly. Each element should have equal probability of returning.
solution.getRandom();

初始想法,采用Math.random()函数,但是后期提交有例子未通过,遂改为利用Random类提供随机数,提交通过,但是这种方法需要知道数链长度且空间要求高。Math和Random的两种随机方法建议采用Random类的随机方法,具体原因可见:Math.random() versus Random.nextInt(int)
故改为第二种方法:水塘抽样法。该方法的原理可参见维基百科数据工程师必知算法:蓄水池抽样Brief explanation for Reservoir Sampling。具体思路如下:
水塘容量k即要保留的数据,也可以理解为采样值。在本题为1。
现在考虑1个输入,则水塘保留,故保留概率P=1/1;考虑2个输入,水塘只能保留1个,所以对于新加入的“2”保留其的概率为1/2;现在考虑3个输入,保留“3”的概率为1/3,这样对于3个输入来说,3的保留概率为1/3正好,现在看看三输入下的前两个数字被保留的概率,对“1”和“2”来说,前一轮被保留的概率为1/2,这一轮被保留的概率为(1-1/3=2/3),所有总的保留概率为1/2×2/3=1/3。正好满足概率均等。所以推论,对k=1的情况,第i个数保留概率为1/i。

  • 1个输入 1(1/1)
  • 2个输入 1,2(1/2)
  • 3个输入 1,2(删去),3(1/3)

以上就是该题的解题思路,再拓展一下,对于要保留(采样)k个数据,推论第i个数保留概率为k/i,例子(k=2)如下:一个数,200%保留;两个数100%保留;三个数,保留“3”概率为2/3;”1”保留的概率为:1/3(“3”被舍弃即”1,2“保留概率)+2/3(“3”保留下来时概率)×1/2(“1,2”挑一个踢走)=2/3,同理得“2”的概率为2/3;四个数,“4”为2/4,剩下两个数的都为:2/3(上轮保留概率)×(2/4(“4”被踢走概率)+2/4(“4”留下概率)×1/2(二选一被踢走概率))=2/4=1/2;总结起来就是:当进行到第i个数时,第i个数的概率为 k/i ,之前数保留概率为 [k/(i1)][(1k/i)+(k/i)[(k1)/k]]=k/i

  • 1个输入 1(2/1)
  • 2个输入 1,2(2/2)
  • 3个输入 1,2(删去),3(2/3)
  • 4个输入1(删去)2(删去),3,4(2/4)

初始思想的算法如下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {

    ArrayList<Integer> list;

    /** @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node. */
    public Solution(ListNode head) {
        list = new ArrayList<>();
        ListNode i = head;
        while(i != null){
            list.add(i.val);
            i = i.next;
        }
    }

    /** Returns a random node's value. */
    public int getRandom() {
        int size = list.size();
        Random random = new Random();
        int index = random.nextInt(size);
        return list.get(index);
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(head);
 * int param_1 = obj.getRandom();
 */

水塘算法如下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    ListNode h;

    /** @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node. */
    public Solution(ListNode head) {
        h = head;
    }

    /** Returns a random node's value. */
    public int getRandom() {
        ListNode cur = h;
        int res = cur.val;
        Random random = new Random();
        for(int count = 1; cur != null; count++){

            if(random.nextInt(count) == 0) res = cur.val;
            cur = cur.next;
        }
        return res;
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(head);
 * int param_1 = obj.getRandom();
 */
import heapq import random import sys import time class ListNode: """ Definition for singly-linked list node. """ def __init__(self, val=0, next=None): """ Initialize the node """ self.val = val self.next = next class LinkedList: """ Linked list class with minimal methods """ def __init__(self, val): """" Initialize the class @param val: the value of head node """ self.head = ListNode(val) def insert(self, val): """ Insert a vale to the head of the linked list @param val: value to be inserted into the linked list """ node = ListNode(val) node.next = self.head self.head = node def pop(self): """ Pop the head of the linked list, and return its value @return: None if head is None The val of head otherwise """ if self.head: node = self.head self.head = self.head.next return node.val def toList(self): """ Convert the linked list to a list @return: the list """ lst = [] node = self.head while (node): lst.append(node.val) node = node.next return lst def mergeKLists(lists): """" Merge K sorted linked list """ # Your code here heap = [] for i, node in enumerate(lists): if node: heapq.heappush(heap, (node.val, i, node)) dummy = ListNode(-1) curr = dummy while heap: val, i, node = heapq.heappop(heap) curr.next = node curr = curr.next if node.next: heapq.heappush(heap, (node.next.val, i,node.next)) return dummy.next if __name__ == "__main__": # We will generate 1000 lists of 1000 elements list_values = list(range(1000000)) random.shuffle(list_values) link_lists = [] K = 1000 length = len(list_values)//K for i in range(K): start, end = i*length, (i+1)*length list_values[start:end] = sorted(list_values[start:end], reverse=True) ll = LinkedList(list_values[start]) for val in list_values[start+1:end]: ll.insert(val) link_lists.append(ll) # Run your merge list algorithm here start = time.perf_counter() ans = mergeKLists(link_lists).toList() stop = time.perf_counter() # Test the result for index, val in enumerate(ans): if index != val: sys.exit("Your merge algorithm did not work.") print(f"Your algorithm passed test in time {stop-start:.5e}.")
最新发布
05-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值