LeetCode 面试题 17.14. 最小K个数(堆排,快排)/剑指 Offer 10- I. 斐波那契数列 /470. 用 Rand7() 实现 Rand10()(拒绝采样,学!!!)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 面试题 17.14. 最小K个数(堆排,快排)/剑指 Offer 10- I. 斐波那契数列 /470. 用 Rand7() 实现 Rand10()(拒绝采样,学!!!)相关的知识,希望对你有一定的参考价值。

面试题 17.14. 最小K个数

2021.9.3 每日一题

题目描述

设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。

示例:

输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

提示:

0 <= len(arr) <= 100000
0 <= k <= min(100000, len(arr))

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/smallest-k-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

topk,堆排,快排
用自带的堆:

class Solution {
    public int[] smallestK(int[] arr, int k) {
        //直观排序,在直观用堆,再直观,快排
        int l = arr.length;
        int[] res = new int[k];
        if(l == 0 || k == 0)
            return res;
        PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> (b - a));
        for(int n : arr){
            if(pq.size() < k){
                pq.offer(n);
            }else{
                if(n < pq.peek()){
                    pq.poll();
                    pq.offer(n);
                }
            }
        }
        for(int i = 0; i < k; i++){
            res[i] = pq.poll();
        }
        return res;
    }
}

快排,竟然一遍过了,顺利的有点难以置信!我其实对我的快排不太自信

class Solution {
    public int[] smallestK(int[] arr, int k) {
        //快排,自己写一下
        int l = arr.length;
        int[] res = new int[k];
        if(l == 0 || k == 0)
            return res;
        select(arr, 0, l - 1, k - 1);
        System.arraycopy(arr, 0, res, 0, k);
        return res;
    }
    
    public void select(int[] arr, int left, int right, int k){
        if(right < left)
            return;
        int t = quicksort(arr, left, right);
        if(t == k)
            return;
        if(t < k){
            select(arr, t + 1, right, k);
        }else{
            select(arr, left, t - 1, k);
        }
    }

    public int quicksort(int[] arr, int left, int right){
        int pivot = arr[left];
        int i = left;
        int j = right;
        while(i < j){
            while(i < j && arr[j] >= pivot){
                j--;
            }
            while(i < j && arr[i] <= pivot){
                i++;
            }
            swap(arr, i, j);
        }
        //到这里i和j是想等的
        swap(arr, left, i);
        return i;
    }

    public void swap(int[] arr, int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

第一次见这样写快排的…学

class Solution {
    public int[] smallestK(int[] arr, int k) {
        randomizedSelected(arr, 0, arr.length - 1, k);
        int[] vec = new int[k];
        for (int i = 0; i < k; ++i) {
            vec[i] = arr[i];
        }
        return vec;
    }

    private void randomizedSelected(int[] arr, int l, int r, int k) {
        if (l >= r) {
            return;
        }
        int pos = randomizedPartition(arr, l, r);
        int num = pos - l + 1;
        if (k == num) {
            return;
        } else if (k < num) {
            randomizedSelected(arr, l, pos - 1, k);
        } else {
            randomizedSelected(arr, pos + 1, r, k - num);
        }
    }

    // 基于随机的划分
    private int randomizedPartition(int[] nums, int l, int r) {
        int i = new Random().nextInt(r - l + 1) + l;
        swap(nums, r, i);
        return partition(nums, l, r);
    }

    private int partition(int[] nums, int l, int r) {
        int pivot = nums[r];
        int i = l - 1;
        for (int j = l; j <= r - 1; ++j) {
            if (nums[j] <= pivot) {
                i = i + 1;
                swap(nums, i, j);
            }
        }
        swap(nums, i + 1, r);
        return i + 1;
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/smallest-k-lcci/solution/zui-xiao-kge-shu-by-leetcode-solution-o5eg/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 10- I. 斐波那契数列

2021.9.4 每日一题

题目描述

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:1

示例 2:

输入:n = 5
输出:5

提示:

0 <= n <= 100

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

记忆化不熟悉,写个记忆化

class Solution {
    public static final int MOD = (int)1e9 +7;
    int[] memo = new int[105];
    public int fib(int n) {
        if(n <= 1)
            return n;
        if(memo[n] != 0)
            return memo[n];
        int res = (fib(n - 1) + fib(n - 2)) % MOD;
        memo[n] = res;
        return res;
    }
}

打表别看简单,上次笔试打表还调了半天…
用静态代码块实现:

class Solution {
    static int mod = (int)1e9+7;
    static int N = 110;
    static int[] cache = new int[N];
    static {
        cache[0] = 0;
        cache[1] = 1;
        for(int i = 2; i < N; i++){
            cache[i] = (cache[i - 1] + cache[i - 2]) % mod;
        }
    }
    public int fib(int n) {
        return cache[n];
    }
}

470. 用 Rand7() 实现 Rand10()

2021.9.5 每日一题

题目描述

已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。

不要使用系统的 Math.random() 方法。

示例 1:

输入: 1
输出: [7]

示例 2:

输入: 2
输出: [8,4]

示例 3:

输入: 3
输出: [8,1,10]

提示:

rand7() 已定义。
传入参数: n 表示 rand10 的调用次数。

进阶:

rand7()调用次数的 期望值 是多少 ?
你能否尽量少调用 rand7() ?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/implement-rand10-using-rand7
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

刚开始我是这样想的,因为生成1到7是随机的,所以生成10个rand7,也就是10到70共61个数都是随机的
但是我仔细想了一下概率,生成10必须每个位置都是1,生成11的话,可以有任何一个位置是2,所以概率比生成10高了。。
这样做不行
那么生成一个rand7,然后乘10,也就是10 20 30 - 70,除以7,不行
竟然不知道怎么做。。重新想想

无可奈何看答案,拒绝采样,又学到了

思想还是找概率相同的10的倍数的个数的数,那么怎么找呢,如何能够生成概率相同的10的倍数个数呢?
已知取7个数是概率相同的,那么7*7,49个位置生成的概率也是相同的,那么取前40个位置的
那如果生成剩下的9个位置的数该怎么办呢,就拒绝掉,重新生成
一个字,妙!!!!!

/**
 * The rand7() API is already defined in the parent class SolBase.
 * public int rand7();
 * @return a random integer in the range 1 to 7
 */
class Solution extends SolBase {
    public int rand10() {
        //拒绝采样
        //思想就是生成概率相同的数,比如7 * 7,能生成49个数,而且生成49个数的概率都是相同的
        //所以取前40个位置的数,每个位置的概率都是1,
        int row, col, idx;
        do{
            row = rand7();
            col = rand7();
            idx = (row - 1) * 7 + col;
        }while(idx > 40);
        return (idx - 1) / 4 + 1;
    }
}

期望看官解吧,感觉写的非常好
https://leetcode-cn.com/problems/implement-rand10-using-rand7/solution/yong-rand7-shi-xian-rand10-by-leetcode-s-qbmd/

很自然的想到,那么加起来行吗,很显然不行,因为加起来数的话,肯定概率不同;如果同样是用位置的话,不如用乘,拒绝的数少

然后看官解的进阶,要减少调用次数,那么就是减少拒绝的次数;第一次结束,拒绝的数是41到49,9个数,那么再调用一次rand7,就可以产生1-63,63个数,保留1-60,剩下3个,那么再产生rand7,就是1-21,剩下1个;只能基于开始循环。这样可以最大限度的减少rand7的调用:

/**
 * The rand7() API is already defined in the parent class SolBase.
 * public int rand7();
 * @return a random integer in the range 1 to 7
 */
class Solution extends SolBase {
    public int rand10() {
        //拒绝采样
        //思想就是生成概率相同的数,比如7 * 7,能生成49个数,而且生成49个数的概率都是相同的
        //所以取前40个位置的数,每个位置的概率都是1,
        int row, col, idx;
        while(true){
            row = rand7();
            col = rand7();
            idx = (row - 1) * 7 + col;
            if(idx <= 40){
                return (idx - 1) / 4 + 1;
            }
            //剩下41-49
            row = idx - 40;
            col = rand7();  //列
            //7*9 = 63
            idx = (row - 1) * 7 + col;
            if(idx <= 60)
                return (idx - 1) / 6 + 1;
            //剩下61-63
            row = idx - 60;
            col = rand7();
            //3*7=21
            idx = (row - 1) * 7 + col;
            if(idx <= 20)
                return (idx - 1) / 2 + 1;
        }
    }
}

评论区看到的:
定义大数为6-10,小数为1-5
先产生一个1-6的数,用于判断是否是大数
然后产生1-5,判断小数应该是多少

class Solution extends SolBase {
    public int rand10() {
        int first, second;
        while((first = rand7()) > 6);
        while((second = rand7()) > 5);
        return first % 2 == 1 ? 5 + second : second;
    }
}

又看到一个令人深思的想法!!!!:

以上是关于LeetCode 面试题 17.14. 最小K个数(堆排,快排)/剑指 Offer 10- I. 斐波那契数列 /470. 用 Rand7() 实现 Rand10()(拒绝采样,学!!!)的主要内容,如果未能解决你的问题,请参考以下文章

C++&Python描述 LeetCode 面试题 17.14. 最小K个数

LeetCode 面试题 17.14. 最小K个数(堆排,快排)/剑指 Offer 10- I. 斐波那契数列 /470. 用 Rand7() 实现 Rand10()(拒绝采样,学!!!)

面试题 17.14. 最小K个数

面试题 17.14. 最小K个数

面试题 17.14. 最小K个数分治法

面试题 17.14. 最小K个数分治法