1.并查集
一种维护集合的数据结构.
顾名思义,主要有两种操作:
1.将两个集合合并.
2.查询某个数在哪个集合/查询两个数是否在同一个集合.
考虑一个这样的问题:
给你n个点,m个关系,每个关系(x,y)属于同一个集合。
最后Q次询问,每次问你两个数是否在同一个集合.
实现过程:
f数组代表每个点所在的集合的父亲节点.也表征我们在哪个集合(这里我们认为每个集合有且仅有一个父亲节点,那么自然的就可以将这个父亲节点看作整个集合的标志).
1.最开始的时候每个数都是独立的一个集合. 线都指向自己
2.将两个数合并.
1.先找到两个集合的父亲节点(fx , fy)
2.根据靠左原则:将f[fy] = fx;
优化:
路径压缩:我们其实只关注每个点他的顶级父亲节点。所以树的形态不重要.
复杂度:比O(nlogn)小点.
应用1:判环
应用2:求连通块个数
2.带权并查集
2.最小生成树
给你一张带权的无向图.让你把所有点都连通。使得边权之和最小.
Kursal算法.
3.最短路
1.Dijstra(单源最短路):动态规划的思想
dist数组代表从开始节点到第i号点的最短路
bk数组代表某个点是否被标记。(被标记的点都是答案已经算出来的点)
1.dist[s] = 0 , 其他都赋值成inf.bk全为0
2.算法开始:
1.从未被标记的集合中找出一个dist最小的点x.
2.将x点标记为1.
3.松弛x点:将所有与他相邻的点v.都尝试通过x来更新:dist[v] = min(dist[v] , dist[x] + w(x , v));
上述过程进行n次即可。(为什么进行n次?)
2.Floyd:
for (int k = 1 ; k <= n ; k++){
for (int i = 1 ; i <= n ; i++){
for (int j = 1 ; j <= n ; j++){
dp[i][j] = min(dp[i][j] , dp[i][k] + dp[k][j])
}
}
}