原题链接:【382. 链表随机节点】:https://leetcode-cn.com/problems/linked-list-random-node/

题目描述:

给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。

进阶:

如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?

示例:

  1. // 初始化一个单链表 [1,2,3].
  2. ListNode head = new ListNode(1);
  3. head.next = new ListNode(2);
  4. head.next.next = new ListNode(3);
  5. Solution solution = new Solution(head);
  6.  
  7. // getRandom()方法应随机返回1,2,3中的一个,保证每个元素被返回的概率相等。
  8. solution.getRandom();

相关知识点:水塘抽样 链表 数学 随机化

解题思路:

一般的想法就是,我先遍历一遍链表,得到链表的总长度 n,再生成一个 [1,n] 之间的随机数为索引,然后再遍历链表,找到索引对应的节点,不就是一个随机的节点了吗?

但是由题目可知,链表十分大,且长度未知,也就说,我们不知道链表长度n,也就没办法取中间的随机数,那么我们应该怎么去取值呢?

由于是链表,所以我们可以一个节点一个节点遍历加载进入内存。虽然我们无法知道链表总长度,但是我们可以知道我们当前遍历的节点的长度i。

比如我们已经遍历了i个元素,可以从这i个元素中随机取了一个元素a,那如果现在再给你一个新元素b,你是继续留着a呢?还是抽取b作为样本结果呢?以什么样的原则来选择a和b呢?你的选择能够保证概率相等吗?

这里就用到了著名的蓄水池抽样算法

蓄水池抽样算法

假设给定一个数据流,数据流长度N很大,如何从中随机选取k个数据,并且要保证每个数据被抽取到的概率相等。

  1. k = 1,只取一个元素时,要保证每条数据被抽取到的概率相等,那么每个数被抽取的概率应该为1/N,只要采取这种策略,只需要遍历一遍数据流就可以得到采样值,并且保证所有数据被选中的概率均为1/N
  2. k > 1,取k个元素时,要保证每条数据被抽取到的概率相等,那么每个数被抽取的概率应该为k / N

算法实现思路为:

  1. 一个一个遍历数据流,在取第i个数据的时候,生成一个01的随机数p,如果p < k / i,替换池中任意一个数替换为第i个数;当p > k / i,继续保留前面的数。直到数据流结束,返回此k个数。但是为了保证计算准确性,一般是生成一个0i的随机数,跟k相比。

图解如下:

本题题解:

注意这里其实就是K=1的情况,取随机数rand,范围为【0,i),随机数+1,变成【1,i】范围,两边都是闭合的,更容易理解。所以,原来的小于k,即小于1,现在+1,四舍五入都变成了1,所以边界判断范围就是,随机数rand=1,则样本替换为当前遍历的节点,否则保留之前的样本节点,继续往下遍历。

其余就都是链表的操作了。

代码实现如下:

  1. //给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。
  2. //
  3. // 进阶:
  4. //如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?
  5. //
  6. // 示例:
  7. //
  8. //
  9. //// 初始化一个单链表 [1,2,3].
  10. //ListNode head = new ListNode(1);
  11. //head.next = new ListNode(2);
  12. //head.next.next = new ListNode(3);
  13. //Solution solution = new Solution(head);
  14. //
  15. //// getRandom()方法应随机返回1,2,3中的一个,保证每个元素被返回的概率相等。
  16. //solution.getRandom();
  17. //
  18. // Related Topics 水塘抽样 链表 数学 随机化
  19. // 172 0
  20.  
  21. //leetcode submit region begin(Prohibit modification and deletion)
  22.  
  23. import java.util.Random;
  24.  
  25. /**
  26. * Definition for singly-linked list.
  27. * public class ListNode {
  28. * int val;
  29. * ListNode next;
  30. * ListNode() {}
  31. * ListNode(int val) { this.val = val; }
  32. * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
  33. * }
  34. */
  35. class Solution {
  36.  
  37. // 需要将链表传递给getRandom方法,所以只能提取作为类变量
  38. ListNode head;
  39. public Solution(ListNode head) {
  40. this.head = head;
  41. }
  42.  
  43. public int getRandom() {
  44. // 定义一个变量,存储采样的结果值
  45. int result = 0;
  46. // 定义一个变量i,标记遍历到了第几个节点
  47. int i = 0;
  48. // 将当前链表head指针赋值给cur
  49. ListNode cur = head;
  50. // 循环遍历链表
  51. while (cur != null) {
  52. i++;
  53. // 取随机数rand 范围 [1, i]
  54. int rand = new Random().nextInt(i) + 1;
  55. // 因为rand最小值为1,这个边界只能取rand = 1
  56. if (rand == 1) {
  57. result = cur.val;
  58. }
  59. // 指针往后移动,遍历下一个节点
  60. cur = cur.next;
  61. }
  62.  
  63. // 返回采样结果
  64. return result;
  65. }
  66. }
  67.  
  68. /**
  69. * Your Solution object will be instantiated and called as such:
  70. * Solution obj = new Solution(head);
  71. * int param_1 = obj.getRandom();
  72. */
  73. //leetcode submit region end(Prohibit modification and deletion)

LeetCode382-链表随机节点的更多相关文章

  1. [Swift]LeetCode382. 链表随机节点 | Linked List Random Node

    Given a singly linked list, return a random node's value from the linked list. Each node must have t ...

  2. Java实现 LeetCode 382 链表随机节点

    382. 链表随机节点 给定一个单链表,随机选择链表的一个节点,并返回相应的节点值.保证每个节点被选的概率一样. 进阶: 如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现? ...

  3. [LeetCode] Linked List Random Node 链表随机节点

    Given a singly linked list, return a random node's value from the linked list. Each node must have t ...

  4. 382 Linked List Random Node 链表随机节点

    给定一个单链表,随机选择链表的一个节点,并返回相应的节点值.保证每个节点被选的概率一样.进阶:如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?示例:// 初始化一个单链表 ...

  5. [LeetCode] 382. Linked List Random Node 链表随机节点

    Given a singly linked list, return a random node's value from the linked list. Each node must have t ...

  6. [CareerCup] 2.3 Delete Node in a Linked List 删除链表的节点

    2.3 Implement an algorithm to delete a node in the middle of a singly linked list, given only access ...

  7. 【链表】在O(1)的时间删除链表的节点

    /** * 在O(1)的时间删除链表的节点 * * @author * */ public class Solution { public static void deleteNode(Node he ...

  8. 剑指Offer:删除链表的节点【18】

    剑指Offer:删除链表的节点[18] 题目描述 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3-& ...

  9. (CSDN迁移) 输入一个链表,从尾到头打印链表每个节点的值

    题目描述 输入一个链表,从尾到头打印链表每个节点的值. 思路1. 翻转链表,使用java自带的翻转函数或者从头到尾依次改变链表的节点指针 /** * public class ListNode { * ...

随机推荐

  1. 绑定socket描述符到一个网络设备

           网络编程中有时明明用eth0的地址来bind一个udp套接口, 可是发出去的包却是从eht1走的, 在网上找到这么一段话解释该问题:           在多 IP/网卡主机上,UDP ...

  2. jquery 实现 <imput>标签 密码框显示/隐藏密码功能

    1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 < ...

  3. 使用Magisk+riru实现全局改机

    前言 提到全局改机,我们想到修改的不是修改Android源码就是利用Xposed改机,前者成本太高,后者只能修改Java层的数据不够彻底.magisk是Android平台上功能强大的工具,利用它可以随 ...

  4. 重新整理 .net core 实践篇——— filter[四十四]

    前言 简单介绍一下filter 正文 filter 的种类,微软文档中写道: 每种筛选器类型都在筛选器管道中的不同阶段执行: 授权筛选器最先运行,用于确定是否已针对请求为用户授权. 如果请求未获授权, ...

  5. application.properties文件配置

    # 服务端口 server.port=8001 # 服务名 spring.application.name=service-edu # 环境设置:dev.test.prod spring.profil ...

  6. vue中axios的post和get请求示例

    POST请求 methods: { isclick() { if (this.account == "" || this.pwd == "") { this.$ ...

  7. 通过小乌龟从本地主机删除远端svn服务器端的代码或图片文件

    先说解决方案,如果通过windows的右键删除,再次Commit还是会下拉下来,所以需要通过小乌龟的删除来删除这个文件,在删除的那个文件夹里再次提交即可. 图片 1. 选中要删除的文件 2. 右键通过 ...

  8. R数据分析:潜类别轨迹模型LCTM的做法,实例解析

    最近看了好多潜类别轨迹latent class trajectory models的文章,发现这个方法和我之前常用的横断面数据的潜类别和潜剖面分析完全不是一个东西,做纵向轨迹的正宗流派还是这个方法,当 ...

  9. vs2012换肤功能,vs2012主题及自定义主题

    vs2012 默认的主题只有浅色和深色两种,深色太黑了,浅色又太亮了!怎么办? 研究不少时间终于找到了怎么编辑vs2012的皮肤 现在开始吧 打开vs2012-工具--扩展和更新 在联机中搜索 The ...

  10. JAVA基础----面向对象复习和IDEA的安装和使用

    1.使用集成开发工具eclipse 1.1.java的集成开发工具很多,包括:eclipse.Intellij IDEA.netbeans..... eclipse: IBM开发的.eclipse翻译 ...