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()(拒绝采样,学!!!)