算法每日一练(2022年1月16日)链表随机节点

2022年1月16日

382. 链表随机节点

🌔题目描述

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

实现 Solution 类:

  • Solution(ListNode head) 使用整数数组初始化对象。
  • int getRandom() 从链表中随机选择一个节点并返回该节点的值。链表中所有节点被选中的概率相等。

示例:

img

输入
["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中的一个,每个元素被返回的概率相等。

提示:

  • 链表中的节点数在范围 [1, 104]
  • -104 <= Node.val <= 104
  • 至多调用 getRandom 方法 104

进阶:

  • 如果链表非常大且长度未知,该怎么处理?
  • 你能否在不使用额外空间的情况下解决此问题?

🎯题解

记录所有链表元素

我们可以在初始化时,用一个数组记录链表中的所有元素,这样随机选择链表的一个节点,就变成在数组中随机选择一个元素。

代码实现
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    List<Integer> list;
    Random random;

    public Solution(ListNode head) {
        list=new ArrayList<Integer>();
        while(head!=null){
            list.add(head.val);
            head=head.next;
        }
        random=new Random();
    }
    
    public int getRandom() {
        return list.get(random.nextInt(list.size()));
    }
}

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

//时间复杂度o(n)
//空间复杂度o(1)
复杂度分析
  • 时间复杂度:初始化为 O(n) ,随机选择为 O(1) ,其中 n 是链表的元素个数。

  • 空间复杂度:O(n) 。我们需要 O(n) 的空间存储链表中的所有元素。

蓄水池算法

使用蓄水池算法,用通俗的语言说一下:

  • 如果我们池子中只有一个数字,那么拿到第一个数字的概率就是100%毋庸置疑。
  • 两个数字50% 三个数字每个数字的几率都是33% 以此类推。。。。

当我们不知道池子里有多少个数字的时候,就需要用蓄水池的算法思想去计算。

  • 当链表前行到第一个数字,此时取第一个数字的几率为100%,那result自然等于这个数字。
  • 前进到第二个数字,那么此时取这个数字的几率自然就为50%(池子里只有两个数字),那么就是50%的几率取新数字,50%的几率保留原本的数字。
  • 第三个数字的时候,33%的几率取当前最新的这个数字,66%的几率保留原本的数字。这66%中:原本的数字有50%的几率是1,有50%的几率是2。也就是此时三个数字的概率都为33%。 通过这个算法,就能达到取数的概率均摊,从而实现随机。

代码实现

class Solution {
    ListNode head;
    Random random;
    public Solution(ListNode head) {
        this.head=head;
        this.random=new Random();
    }
    
    public int getRandom() {
        // 蓄水池算法
        // 第一次拿第一个数 第一个数的概率是100%
        // 第二次 拿第二个数 取第二个数的概率是50%
        // 第三次 拿第三个数是 33% 则保留原本数的概率是66%
        ListNode p=this.head;
        int count=0;
        int res=0;
        while(p!=null){
            count++;
            int randomint=random.nextInt(count)+1;//因为生成的是[0,count)的值 而不包含count  所以要加1
            if(randomint==count){
                res=p.val;
            }
            p=p.next;
        }
        return res;
    }
}
//时间复杂度o(1)
//空间复杂度o(1)
复杂度分析
  • 时间复杂度:初始化为 O(1) ,随机选择为 O(n) ,其中 n 是链表的元素个数。

  • 空间复杂度:O(1) 。我们只需要常数的空间保存若干变量。

🐄我的看法

今天的题目是关于数据结构的一道题目,我并没有看的太懂,只是知道了大概的思路,Java数据结构我不是太了解,就不过多的解释了,我把官方的题解整理了一下,仅供参考。

加油,每天进步一点点!🎃

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值