求解一个区间的和乘以这个区间最小值的最大值
解题思路:单调栈 + 前缀数组
i到j的区间和,通过前缀数组,比较容易在O(1)的复杂度内取得。
对每一个元素,取得左边第一个比他小的元素,作为j;从后往前遍历,递增栈;
对每一个元素,取得右边第一个比他小的元素,作为i;从前往后遍历,递增栈;
整体时间复杂度$O(n)$
求解一个区间的和乘以这个区间最小值的最大值
解题思路:单调栈 + 前缀数组
i到j的区间和,通过前缀数组,比较容易在O(1)的复杂度内取得。
对每一个元素,取得左边第一个比他小的元素,作为j;从后往前遍历,递增栈;
对每一个元素,取得右边第一个比他小的元素,作为i;从前往后遍历,递增栈;
整体时间复杂度$O(n)$
关于队列一共有两类问题
队列的特点:先进先出
队列的用途:
1 |
|
借助两个栈来实现一个队列
利用一种特殊的队列(单调队列)来巧妙的解决滑动窗口最值问题。
请用栈实现一个队列,支持如下四种操作:
push(x) – 将元素x插到队尾;
pop() – 将队首的元素弹出,并返回该元素;
peek() – 返回队首元素;
empty() – 返回队列是否为空;
注意:
你只能使用栈的标准操作:push to top,peek/pop from top, size 和 is empty;
如果你选择的编程语言没有栈的标准库,你可以使用list或者deque等模拟栈的操作;
输入数据保证合法,例如,在队列为空时,不会进行pop或者peek等操作;
样例
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // returns 1
queue.pop(); // returns 1
queue.empty(); // returns false
1 | class MyQueue { |
1 | //单调队列 —— 模板题 AcWing 154. 滑动窗口 |
什么是LRU缓存,怎么设计的LRU缓存。
时间复杂度:$O(1)$
1 |
|
二叉堆,包括最大堆、最小堆
最大特点:在$O(1)$时间内找到最小(最大)元素
堆的常见应用:
1 | 堆 —— 模板题 AcWing 838. 堆排序, AcWing 839. 模拟堆 |
给定一个长度为 n+1 的数组nums,数组中所有的数均在 1∼n 的范围内,其中 n≥1。
请找出数组中任意一个重复的数,但不能修改输入的数组。
样例
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
思考题:如果只能使用 O(1) 的额外空间,该怎么做呢?
思考: 把肯定不符合条件的一半给忽略掉;
1 | class Solution { |
给定一个长度为 n 的整数数组 nums,数组中所有的数字都在 0∼n−1 的范围内。
数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。
请找出数组中任意一个重复的数字。
注意:如果某些数字不在 0∼n−1 的范围内,或数组中不包含重复数字,则返回 -1;
样例
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
1 | #include <iostream> |
问题1: 给定一个函数rand()能产生1到m之间的等概率随机数,产生1到n之间等概率的随机数?
1 | int rand10() |
考虑如果rand()返回的是浮点呢?
这种情况下可以简化为,如何把[a,b]线段上的点等概率的映射到[c,d]
另外, 为啥不能考虑rand() * rand()呢?
因为不等概率,76与67的计算结果都是42,这样子就使得出现42的概率是1/49 + 1/49;出现1的概率是1/49。
参考:
https://www.cnblogs.com/double-win/archive/2014/04/07/3650314.html
https://my.oschina.net/u/4401597/blog/4038277
问题2: 有一个随机生成器randA(),以p的概率返回0,1-p的概率返回1,利用这个randA()构造randB(),使randB()等概率的返回0和1,即0.5的概率返回0,0.5的概率返回1。
分析比较简单: 出现00的概率是p^2; 出现10的概率是p(1-p),出现01的概率是p(1-p); 出现11的概率是(1-p)^2;
以上,只要丢弃掉00,01的结果; 10与01的出现概率就是相等的;也就各为50%。
1 | int randB() |
已知一随机发生器,产生0的概率是p,产生1的概率是1-p,
现在要你构造一个发生器,
使得它构造0和1的概率均为 1/2;
构造一个发生器,使得它构造1、2、3 的概率均为 1/3; …,
构造一个发生器,使得它构造 1、2、3、…n 的概率均为1/n,要求复杂度最低。
思路:
由于需要产生1/2,而用1位0,或1位1无法产生等概率,
因此,考虑将随机数扩展成2位:
00 pp
01 p(1-p)
10 (1-p)p
11 (1-p)(1-p)
有上述分析知道,01和10是等概率的,因此我们只需要产生01和10就行了。
于是可以,遇到00和11就丢弃,只记录01和10。可以令,01表示0,10表示1,则等概率1/2产生0和1了。
对于n=2,一次性生成两个数字,认为01表示0,10表示1,
其它情况放弃,它们的概率都是p(1-p);
对于n=3,一次性生成三个数字,认为001表示0,010表示1,100表示2,
其它情况放弃,它们的概率都是pp(1-p);
对于n=4,一次性生成是个数字,认为0001表示0,0010表示1,0100表示2,1000表示3,
其它情况放弃,它们的概率都是ppp(1-p);
5为例,此时我们取x=2,因为C(2x,x)=C(4,2)=6是比5大的最小的x,
此时我们就是一次性生成4位二进制,把1出现个数不是2的都丢弃,
这时候剩下六个:0011,0101,0110,1001,1010,1100,
取最小的5个,即丢弃1100,那么我们对于前5个分别编号1到5,
这时候他们的概率都是pp(1-p)*(1-p)相等了。
关键是找那个最小的x,使得C(2x,x)>=n这样能提升查找效率。
因为C(n,i)最大是在i接近n/2的地方取得,此时我有更大比率的序列用于生成,
换句话说被抛掉的更少了,这样做是为了避免大量生成了丢弃序列而使得生成速率减慢,
实际上我之所以将x取定是为了让我取得的序列生成的概率互相相等,
比如C(2x,x)的概率就是[p(1-p)]^x,
互等的样例空间内保证了对应的每个值取得的样例等概率。
一个从1到n的序列,随机打乱,保证每个数出现在任意一个位置的概率相同。
基于交换,将a[i]与arr[rand(i, n - 1)]随机交换;
1 | // 得到一个在闭区间 [min, max] 内的随机整数 |
问题3: 程序的输入包含两个整数m和n,其中$m \lt n $。输出是0~n-1范围内m个随机整数的有序列表,不允许重复。从概率的角度来说,我们希望得到没有重复的选择,其中每个选择出现的概率相等。
选出这个序列的概率是:$m/n (m-1) / (n-1) … * (1)/(n-m)$
1 | void generate(int m,int n) |
问题4: 如何数据流中随机选出N个元素
蓄水池采样算法,具体步骤如下:
(1)先把N个坑位填上
(2)对于后面新来的第i元素,做如下概率处理:令X=rand(0, i), i > N
A. 如果X在[0, N]之间,则用swap(X, i)
B. 否则啥也不做
1 | int i = 0; |
深入一下,分布式蓄水池算法: https://www.jianshu.com/p/7a9ea6ece2af
You are given a fair coin. Can you design a simple game using the fair coin so that your
probability of winning is p, 0 < p < 1
把p表示为2进制小数;例如0.100011100110101
开始抛硬币,正面1; 反面0,计作s[i]
如果第i次抛出1,而p[i] = 0, 则win;
如果第i次抛出0,而p[i] = 1, 则lose;
如果s[i] == p[i],则继续新的循环
男孩女孩问题
如果一个地区所有人都生了女孩就继续生,直到生出男孩立马停下,这个地区会趋于什么样的男女比例?
1:1 ,几何分布
https://www.zhihu.com/question/355605125
一根绳子分三段,能够合成三角形的概率
线性规划 + 几何概率, 1/4
如果在高速公路上30分钟内到一辆车开过的几率是0.95,那么在10分钟内看到一辆车开过的几率是多
算法题2:给定k个数组,每个数组都是有序的,且每个数组最大值-最小值<1000,1 < k<1000,求所有数的中位数。
解答思路:
定义一个结构体 NumCount {
int number;
int cnt;
}
预处理所有数据,将原来的数据用NumCount nums[k][1000]来保存。
用一个大顶堆来维护K路的最小值。
时间复杂度:$O(1000 * k)$