二叉堆,包括最大堆、最小堆
最大特点:在$O(1)$时间内找到最小(最大)元素
堆的常见应用:
- 堆排序
- 用两个堆(一个最大堆,一个最小堆)来维护/查询第K大/小的操作
- 贪心算法优化
1 | 堆 —— 模板题 AcWing 838. 堆排序, AcWing 839. 模拟堆 |
二叉堆,包括最大堆、最小堆
最大特点:在$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分钟内看到一辆车开过的几率是多
https://preshing.com/20111007/how-to-generate-random-timings-for-a-poisson-process/
https://preshing.com/20111118/locks-arent-slow-lock-contention-is/
算法题2:给定k个数组,每个数组都是有序的,且每个数组最大值-最小值<1000,1 < k<1000,求所有数的中位数。
解答思路:
定义一个结构体 NumCount {
int number;
int cnt;
}
预处理所有数据,将原来的数据用NumCount nums[k][1000]来保存。
用一个大顶堆来维护K路的最小值。
时间复杂度:$O(1000 * k)$
0,1,2
分为三段:0段、未判定段、2段,用三个指针(s0, cur, s2)指向每一段的当前位置。
s0并不是指向0段的最后一个元素,而是指向最后一个元素的下一个元素。s2类似。
如果A[cur]=0,则s0(其实指向1),cur互换位置; s0++; cur++;
如果A[cur]=1,则cur++;
如果A[cur]=2,则s2,cur互换位置; s2—; cur不变;
有字符串,将所有连续的ac跟单独的b去掉后的字符串:如bbbacccccb->ccc; aacceacdby->edy
时间复杂度O(n) 空间复杂度O(n) —> 时间复杂度O(n) 空间复杂度O(1)
这是本题较好的一种解法,设两个指针cur和loc分别从头开始出发,cur每次移动一格,另一个指针loc保留当前的操作位置,如果cur指向的字符是c且loc指向的是a,则将loc回移一位(ac抵消了),如果遇到其他非b的字符,则将loc处的字符置为cur处的字符(++location),一直进行直到到cur到达字符串尾部,此时取字符串开头到loc指针之间的子串即为本题的解。这种解法妙就妙在loc处的字符是即时更新的,一些边界条件都自动消除了。
输入n个整数,输出出现次数大于等于数组长度一半的数。
输入描述:
每个测试输入包含n个空格分割的n个整数,n不超过100,其中有一个整数出现次数大于等于n/2。
输出描述:
输出出现次数大于等于n/2的数。
摩尔投票法的核心就是一一抵消,删除不同的数。
因为要找过半的数,用一个变量count记录读取每个变量变化的次数,一个变量temp记录可能过半的数。
先让count = 1,然后让temp = vec[0],然后往后遍历一遍。
碰到和temp相同的数就给count++,否则就count—,如果count变成0,就让temp=vec[i] (v数组遍历过程中的当前值),并让count = 1。
如此遍历一遍,因为有一个数过半,所以temp最后肯定存储的是过半的数
1 | class Solution { |
参考:
https://www.zhihu.com/question/284969980/answer/440979325
给定一个包含了一些 0 和 1 的非空二维数组 grid 。
一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)
解题思路:
DFS
稠密图,邻接矩阵;
稀疏图,邻接表
1 |
|
1 | 树与图的遍历 |
1 | 拓扑排序 —— 模板题 AcWing 848. 有向图的拓扑序列 |
| 算法 | 解决的问题 | 算法原理 | 时间复杂度 |
|---|---|---|---|
| dijkstra | 单源最短路,稠密图, 正权 | 找到最小距离,用找到的最小距离更新剩余距离 | $O(N^2)$ N为图点的数量 |
| 堆优化版dijkstra | 单源最短路,稀疏图,正权 | 借助堆来优化dijkstra算法中的查找最小值操作 | $O(N^2)$N为图点的数量 |
| bellman-ford | 单源最短路,负边权,限制步数的情形 | 迭代k次,每次找最小距离 | $O(N^2)$ N为图点的数量 |
| SPFA | 单源最短路,负边权,判定有没有负环 | 队列优化 | $O(N^2)$ N为图点的数量 |
| floyd | 多源汇最短路 | 动态规划思想,三角不等式 | $O(N^3)$ N为图点的数量 |
1 |
|
| 算法 | 解决的问题 | 算法原理 | 时间复杂度 |
|---|---|---|---|
| prim算法 | 单源最短路,稠密图, 正权 | 找到最小距离,用找到的最小距离更新剩余距离 | $O(N^2)$ N为图点的数量 |
| kraskal算法 | 单源最短路,稀疏图,正权 | 并查集 | $O(N^2)$ N为图点的数量 |
1 |
|
如何判定图是不是二分图:DFS遍历,奇数环
二分图匹配:匈牙利算法
1 | 染色法判别二分图 —— 模板题 AcWing 860. 染色法判定二分图 |