算法1-B
BST && BBST 二叉搜索树 平衡二叉树
-
从根开始
-
根据比较的大小,来决定是往左(左子树)还是往右(右子树)
-
逐级加深比较
-
越深的地方,所需要的比较次数也就越多,因此倾向于构造平衡、偏平的二叉树
Hashtable + Dictionary + Map
- 根据一个"名字",快速的找到一个元素
- 散列表,或哈希表
- 时间复杂度O(1)
- 哈希冲突:两个元素在同一个位置存储,此时会像拉链条一样往后存储
- 关键点,怎么取模?
- 取模值一般是散列表长度
- 一般是素数
- 具体为什么是素数,需要去看完整的数据结构课
- python中有Dictionary 、map这种数据结构,底层原理就是hashtable
priority queue - heap
- huffman tree ---- 维护了一个森林
- 选出最小的两棵树(最开始是节点),构造一个新的节点,组织成一个新的二叉树
- 花费时间 是 (n-1)*n
- 三角形的形态(堆的样子、树的样子)
- 优先队列
- 查找一个元素时,是看谁的优先级更高?
Huffman : stack+queue
-
2 5 13 16 19 37(根节点的左子树的全部叶子节点)
xx xx xx xx xx
-
13 16 19 37
*7 xx xx xx
-
16 19 37
20 xx xx
-
37
*20 *35 xx
-
37
* 55
-
* 92 带*号的都是父亲节点
minimum spanning tree 最小支撑树
-
图
-
背景:每一条边都是有代价的,比如修路,这就是一张有权图,如何从整个图中选出合适条边作为连通图
-
1.边不要太少,要保证连通
2.边要有权重,有好坏,要保证权重最小
3.边不能太多,不能有环
prim:cut+cross
- 图论中知识,cut:割
- 一旦有割,就会有边(cross)
prim方法
prim算法实例(跨边cross最小的是割cut)
-
在所有点中找到一个点(u),其余剩下的点相对于这个点构成了一个集合(v) 教材第九页
-
u集合到v集合所有边中权重最小的边取出,构成了一条割(cut),然后就会多一个节点,放到u集合,同时v集合就会少一个节点
-
然后找出新u到新v所有边中权重最小的边,此时u集合又会多一个点,v集合又减少一个,
-
一直进行下去,当扩无可扩,v集合为null后,形成了一个mst
-
因为可以从任意节点出发,所以形成的mst不唯一
-
任何一个带权图都可以表示成一个矩阵,同样反过来,每一个矩阵都可以表示一个带权图
-
无权重的点,认为等于无穷
prim方法生成mst的贪心写法
-
邓公的excel解法
-
从第一条片开始,即从0号点出发
-
可以找到权重最小的cross,得到一个节点k
-
同时cross边变多了,从而去新的最小的cross,又得到一个节点l,同样跨边又增加了,又去找最小的cross(找出最小边的过程等同于,新引入的点如果坐标是a,b,如果a行已被染色,那么应该让b行、b列都染色,都会被染色,注意是二维的,去找出染色点中最小值所在的节点)
-
dist(v) = min{|v,u| 任意的u}
-
delmin()方法的复杂度logn,找出最小的边
Heap
- prioriti queue 优先队列 简称P.Q.
Kryskal
sorting by weight
-
不同于prime,从点开始找,然后引入合适的点,kryskal是直接从边开始找,即直接引入边
-
在prime算法,迟早会碰到那条最小的边,此时一个点在在u中,一个点在v中,所以最小那条边一定会被mst采用
-
进而贪心的找第二条小的边,无论是最小的边和第二小的边有公共点,和没公共点,第二小的边都会被吃进去
-
出现第三小的边,就不一定会采用了,不是简单的数到n-1,此时需要判断,因为有可能构成环!此时第四条边、第五条边、第六边…构成环的概率越来越大
-
先按权重排序,找出最小权重的点
-
选出最小的边,如果构成环了,即使是最小的边,也会删掉,重新去寻找下一个最小的边,等同于找出的最小的边需要连接的两个点是来自于同一棵树种的两个点。
-
邓公的excel演示
-
上三角矩阵(含对角线)
-
取出对角线,剩下的元素代表对角线任意两个点的连线,代表权重值
-
然后依次找出权重值中的最小值(所有边中最小的cross),
-
然后找到对应的i,j,形成一个队列,一直这样进行下去,中间过程中可能会存在多个队列(因为点到点形成的集合可能会存在多个互斥的u),但是最终之前一定会合并成一个树,
-
如果找边的过程,有一条边的两个点同时存在于某一个u当中时,这条边会被舍弃
-
最后当集合u中已经包含了所有对角线元素时,则已经形成了n-1条边,此时则归成一根棍了,此时即可结束寻找边的过程
-
-
算法很精妙,不断在合并小的u,所以适用于稀疏矩阵
union-find
- 合并互斥的u,u是找当前最小的cross时形成的节点集合
- 一种新的数据结构,并查集,一个u,即一颗树,找出一个节点作为统一标识,如果试图连接两个节点,但是返回的值是同一个,此时就不会连接,这就是并查集,如果不同节点查回来的值不相等,说明不是同一棵树,此时就可以将这两个子集合并起来,所以并查集(独立集)可以很高效的实现kryskal算法
quick-find
- 对角线元素每一个元素都可以看出最初的并查集
- 随便两个节点,其中一个节点跑到另一个节点的集合中去,此时跑过去的节点的值也更新成另一个节点的值,实际可以都用下标值表示,更加的清晰
slow-union
- 虽然子集可以快速的膨胀,既在常数时间内做到O(1)
- 但是子集合并的时候,随着子集的元素数量增加,合并起来会变得越来越慢,此时是拖家带口,而且是反复的搬,所以复杂度会很高,会受到时间惩罚
- 这种拖家带口的复制方法,很像数组,如果在某一个位置上插入数组,需要整体挪动数组,这种操作很需要时间,
quick-union
-
用链表的方式来拼接元素,而不是用数组
-
无论哪一个节点沿着链表向上走,最终都可以找到最上面的节点,
-
班级合并时,让去合并的班长指向合并后的班长,即只改变班长,此时union相比数组合并会变快
-
班长是自己指向自己的
-
no traide off,次长彼短,此时每一个点去查找的复杂度取决于它的深度,不再是O(1)
-
所以此时多叉树的数据结构是更优的,
-
多个子集合并时,取决于树的高度,应该讲矮树往高树合并,
Path compression 路径压缩
- 最好的结构,得到的是多叉树,1节点与其他所有节点相连(最优,)
- find操作,去一棵树中向上找自己的班长,尽量让这条路径极可能折叠,
- 当某一个班长,和另一个班长合并时,将某一个班长的所有成员全都指向合并后的班长,此时就完成了一次路径压缩