OJ Tricks
这里主要是一些题目的技巧总结。
min max heap usage
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值
排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之
后中间两个数的平均值。
一个大堆,一个小堆,保证 大堆的头永远比小堆的头最小,并且两者数量接近(差一,或者相
等)保存4个变量,两个堆的数量,以及堆的顶值,
min_size, max_size, min_top. max_top
当获得一个新的数,如果小于 min_top, 就放进 max_heap
中,但是可以判断一下和 max_top 的大小,这样可以更新它
然后在做判断, 是否 max_size > min_size,
是的话,就把 max_top 放入 min_heap 中,它当然是最小的,所以可以更新
这时候,我们再更新, max_size, min_size, 这样即可保证两者数量接近
并且俩个堆也一定是连续的
max_top| |min_top 两者构成的区间,在不停的移动~~ 两堆数量相近
N 个有序队列的合并,
一个最小堆维持N个队列的当前头节点,每次从中取出最小的,
判断来自那个队列,将新的头节点放入,然后更新队列指针,
以此重复
还有一个用途 就是 图里面最小生成树的时候, 所以大小堆的
使用场景比较明显,就是需要找最大,或者找最小的时候,其它的
元素我们都不太在乎
找出数组 K 个小元素
同理,先拿K个元素放进最大堆,每次和 max_top 对比,
比他小则放入,头部则被 pop 出来,其实不一定要用堆~
DP
DP 很重要的思想在于,从 N -> N+1 或者能从 N -> N-1 ,另外 DP 代表的就是计算机思维,这是非常重要的,计算机的思维也是迭代的思维,这种思想其实非常的重要,我们永远都不会去寻找一个精确值,而是不断的从上一次值来推出下一个更加合适的值~
长度为 N 的区间,现在有 M 个 [xi, xi+1] 的区间,是判断能否覆盖,并且最少需要的
个数选出。
DP: 先考虑 0 - xi 的区间,然后考虑 x1 -> N
也就是选择从0出发,最长的一区间 len,然后考虑
len - N 以此类推,这是将区间砍成一份一份,其实
就是逼近的思想,操作就是将其余的区间都减去当前
已经覆盖的,重新排序, 然后问题又变成了
0 - (N-xi) 的问题,直到最后选择完毕~
全排列这里不说了,下面说说 , AABCDD 的组合数计算
假设选 M 个数,我们选第一个数之后,马上变成选 M -1
个,这就是迭代(递归就是迭代的实现方式),
我们只需要记下,当前的位置即可,以及曾经选择过的字母
1. A -- 当递归回来,下一个是A,即会判定重复
2. A B C D D
第二层是可以选A的,不同层之间不需要判断重复,
关键是,比如选择了D之后,子递归已经处理完了。
然后下一个选D,我们就得判定重复,简单的说
就是函数体自己判断重复即可,子函数只需要知道有
哪些可以选择即可
如果说是有重复元素的重排列呢? 很简单,我们
在 1 个元素的时候,不需要排列,
m个元素的时候, 我们将m 和 m-1 个元素都互换一次,然后
做 m-1 排列,
我们只需要保证, m 和 [m-1] 这个集合不出现重复的集合即可,
因为 A[m-1] 集合已经全排列过了
两种情况, 1. m 和 A[i] 相同,那是白费力气,上面说过已经排列过了
2. A[i-1]内有重复元素,m和它们都交换了,那就是出现了重复集合了。
所以, m 得维持一个集合,来判断跟他们交换与否,一个数组即可~
还有一类就是背包问题,
0
0 0
0 0 0
0 0 0 0
比如上述的节点都有一个值(路径),然后从顶部出发,计算最小的路径,
每次只能选择其左右的两个节点,其实可以抽象为一个矩阵
0 0 0 0
0 0 0
0 0
0
上面都有数值,这不就是填表么, 比较 x4 x7 x9 x10的大小即可
x1 x2 x3 x4
x5 x6 x7
x8 x9
x10
还有别的方案,就是多 -> 少,即最底出发,每次就可以淘汰掉一个节点
也就是说, 第三层和第四层的组合数其实很少,俩俩比较就可以淘汰一个
以此迭代。但是这里主要说的 少-> 多
少 -> 多 也就是上面填表的过程,我们思想是这样的,
我们在第 N 层,我们考虑,N - 1 层的最短路径,然后我们在把
N 节点的长度,加上,选择出最小的
所以问题就变成了,我们要算的就是,第一层怎么到第二层呢?
以此类推。
现在延伸一下,变成方格, M * N
0 0 0 0
0 0 0 0
0 0 0 0
那么上面的 多到少就不适用了,中间截断也只适合方形矩阵
这个 a[i,j] = min{a[i-1,j], a[i, j-1]} + value(a[i,j])
跟上面是一样的,还是填表,
x1 x2 x3 x4
x5 x6 x7 x8
x9 ....
其实只需要长度为 N 的一维数组即可,因为可以覆盖,但是算法书上
那个背包就不可以, 因为它还需要之前的数值,我们只需要俩个数值
即可。
总结一下,一般只是最优解,可以填表,但是不一定~ 我觉得两者结合应该是最舒服的吧。
Last updated