398. 随机数索引 / 382. 链表随机节点 / 剑指 Offer II 016. 不含重复字符的最长子字符串

398. 随机数索引【中等题】【每日一题】

思路:

方法一:【哈希表】

用哈希表存储每个数及其对应的索引,然后在target对应的索引里边随机返回一个索引作为答案。

代码:

class Solution {

    Map<Integer, List<Integer>> map;
    Random random = new Random();

    public Solution(int[] nums) {
        map = new HashMap<>();
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            map.putIfAbsent(nums[i], new ArrayList<>());
            map.get(nums[i]).add(i);
        }
    }
    
    public int pick(int target) {
        //随机输出索引
        List<Integer> indexs = map.get(target);
        return indexs.get(random.nextInt(indexs.size()));
    }
}

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

方法二:【蓄水池抽样】

依次遍历所有的数字,如果遇到target就将计数变量cnt1,这样可以统计所有的target出现的次数,我们只要将这cnttarget保持概率均等抽取即可,即我们要返回的答案索引index被选中的概率应为1/cnt

那么我们只需保持每次抽到target时,当选中概率为当前的1/cnt时,将答案索引index更新为当前索引即可。

如何确保选中概率为当前的1/cnt,只需在[0,cnt)范围内随机抽取到的数是0即可。(当然也不一定非得是0,只要在那个范围内就行,反正概率都一样,取0是为了表述方便,也可以取尾端cnt-1,不影响最后的求解)

代码:

class Solution {

    Random random = new Random();
    int[] nums;

    public Solution(int[] nums) {
        this.nums = nums;
    }
    
    public int pick(int target) {
        int n = nums.length,index = 0,cnt = 0;
        for(int i = 0; i < n ; i++){
            if(nums[i] == target){
                cnt++;
                if(random.nextInt(cnt) == 0){
                    index = i;
                }
            }
        }
        return index;
    }
}

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

382. 链表随机节点【中等题】

思路:【蓄水池抽样】

与上题一个套路,具体实现不做讲解,写本题是为了巩固知识。
另外提交的过程中必须将head再拷贝一遍,要不然无法返回正确结果,本人较菜懒得研究是为什么了,对比题解之后我将head再拷贝一遍(ListNode t = head;)就可以返回正确结果了。

代码:

/**
 * 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 {
    Random random = new Random();
    ListNode head;
    public Solution(ListNode head) {
        this.head = head;
    }
    
    public int getRandom() {
        int ans = 0,cnt = 0;
        ListNode t = head;
        while(t != null){
            cnt++;
            if(random.nextInt(cnt) == 0){
                ans = t.val;
            }
            t = t.next;
        }
        return ans;
    }
}

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

剑指 Offer II 016. 不含重复字符的最长子字符串【中等题】

思路:【滑动窗口】

先固定左边界,再固定右边界,然后每次左边界右移1位,对应更新右边界,求出每一个符合题意的子串,并将其长度与max比较,更新max,最后返回max即可。

代码:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length();
        if (n <= 1){//长度为0,返回0,长度为1,返回1
            return n;
        }
        // 哈希集合,记录每个字符是否出现过
        Set<Character> set = new HashSet<>();
        //窗口右边界,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        int right = -1, max = 0;
        for (int left = 0; left < n; left++) {//每次遍历更新窗口左边界
            if (left != 0) {
                //窗口左边界右移1位,对应set中删除原左边界字符
                set.remove(s.charAt(left - 1));
            }
            while (right + 1 < n && !set.contains(s.charAt(right + 1))) {
                // 不断地扩大右边界,并将遇到的字符加入set集合,直到遇见重复的为止,此时right位置字符与前边字符重复,退出while循环
                set.add(s.charAt(right + 1));
                right++;
            }
            // [left,right)区间窗口是一个极长的无重复字符子串,其长度为right-left+1
            max = Math.max(max, right - left + 1);//更新max
        }
        return max;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值