图的表示: 稀疏图 邻接表
稠密图 邻接矩阵
深搜 DFS
实现:递归
非递归
应用:
连通分量
广搜 BFS
实现: 队列
无向图
表示:邻接表/邻接矩阵
可达性: DFS (BFS也可)
单点最短路径:BFS
连通分量: DFS (BFS 并查集)
有向图:
表示:邻接表/邻接矩阵
可达性: DFS (BFS也可)
单点最短有向路径:BFS
检查有无环:DFS
拓扑排序:
定义:
对所有顶点排序 使得任意有向边都是从排在前边的顶点指向排在后面的元素
解决方法:
1. 入度为0的入队方法 c++数据结构 p320
2. DFS的逆后序 递归之后压入栈
必须是无环图 才有拓扑排序
有向无环图 把所有顶点排序,保证所有的有向边都是排在前面的顶点指向后面的顶点
1)用队列实现,入度为0的顶点入队, 然后进行循环,条件队列不为空,第一个元素出队,同时设为已访问且放进一个数组A里面,对这个元素的所有领接点入度减1,假如入度为0 了,就入队,
退出循环之后,遍历所有点,看有没有没访问的,有就是有环,也就没有拓扑排序,没有那就输出数组A,就是拓扑排序
2)用DFS的逆后序,这个方法首先需要检查有没有环
检查的方法是,加一个bool数组,数组的size为节点个数的Onstack,还有个int * cycle 放找到的第一个有向环 ,然后对所有的节点,没有被mark的v,挨个做DFS,具体是,把这个onstack[v]和mark[v]设为true,然后遍历它的邻接点w,假如有cycle不等于nullptr,也就是已经找到环了,直接return,假如没有,再检查有没有被mark过else if,没有就edge[w] = v,再递归做dfs, 再检查w有没有在栈里面 else if (onstack[w] ) 在就是找到环了(因为意味着前面存在w到v,然后现在v到w,所以是个环),然后从v开始 沿着edgeto 直到w 放进cycle,最后再放v。
最后把onstack[v] 重新变回false
检查如果没环,这时候再用dfs,这时候,每个递归完之后,把这个v压入栈里面,最后把栈弹光就是图的拓扑排序、
tips:
(有向无环图才有拓扑排序)
强连通分量:
定义:
分量中任意两个顶点都是强连通分量(存在a->b, 也存在b->a,这叫做强连通分量)
解决方法:
kosaraju算法:有向图找强连通分量
1) G^R(反向图) 求拓扑排序
2)按上面的排序 对G 进行DFS
kosaraju(实际用了两次DFS)
加权无向图:
(一般求最小生成树)
表示:邻接表 但链表节点是edge
prim算法和kruskal算法
最小生成树
只有连通图(从图的任意一个顶点能到任意另一个顶点)才存在最小生成树
生成树是图的一颗含有其所有顶点的无环连通子图,最小生成树,就是权值和最小的生成树
子图(图 边的一个子集(以及边上的那些顶点))
我们讨论的最小生成树是在一副加权连通无向图中的,(权值相不相同,无所谓,权值是正是负也无所谓)
两个算法不能处理有向图,所以一般最小生成树都是针对加权无向图而言的都基于两个基础
1)图的任意切分,横切边权重最小者必定属于最小生成树
2)贪心算法:命题k p392 每次找到一种切分,它的横切边 均不为黑色(黑色代表MST的一个边),把最小权值的横切边涂成黑色,重复,直到有 顶点数-1个边为止(最小生成树的边数量一定是顶点数-1)
kruskal算法:
基本思路,把所有边由小到大排序,每次取最小的边,假如不会和已经在MST中的边形成一个环,那就加进来,下一个,假如会形成环,抛弃,下一个。循环直到有 v-1个边
具体方法:最小堆把边排序,并查集识别会形成环的边,队列保存MST结果
时空复杂度:v个顶点,E条边
空间: E
时间: ElogE (最坏)
prim算法:
从节点0开始 每次把 有且只有一个节点在MST里面的边,且这个边是符合条件的最小权值,这个边加到MST里面,直到有V-1个边
lazy实现 时空复杂度:v个顶点,E条边 空间: E 时间: ElogE (最坏)
eager实现: 空间:v 时间ElogE(最坏)
加权有向图(一般目的就是求最小路径树)
表示:邻接表,链表节点是有向edge
权重非负:Dijkstra算法 (将distTo[] 最小的非树顶点放松,并加入树中)
无环(权值可以负):按拓扑顺序 放松顶点
一般情况(权值可以负,可以有环,但别有负权重环,存在负权重环,最短路径树就不存在了):Bellman-Ford算法
https://blog.csdn.net/a60782885/article/details/72781811
最小生成树
只有连通图(从图的任意一个顶点能到任意另一个顶点)才存在最小生成树
生成树是图的一颗含有其所有顶点的无环连通子图,最小生成树,就是权值和最小的生成树
子图(图 边的一个子集(以及边上的那些顶点))
我们讨论的最小生成树是在一副加权连通无向图中的,(权值相不相同,无所谓,权值是正是负也无所谓)
两个算法不能处理有向图,所以一般最小生成树都是针对加权无向图而言的都基于两个基础
1)图的任意切分,横切边权重最小者必定属于最小生成树
2)贪心算法:命题k p392 每次找到一种切分,它的横切边 均不为黑色(黑色代表MST的一个边),把最小权值的横切边涂成黑色,重复,直到有 顶点数-1个边为止(最小生成树的边数量一定是顶点数-1)
kruskal算法:
基本思路,把所有边由小到大排序,每次取最小的边,假如不会和已经在MST中的边形成一个环,那就加进来,下一个,假如会形成环,抛弃,下一个。循环直到有 v-1个边
具体方法:最小堆把边排序,并查集识别会形成环的边,队列保存MST结果
时空复杂度:v个顶点,E条边
空间: E
时间: ElogE (最坏)
prim算法:
从节点0开始 每次把 有且只有一个节点在MST里面的边,且这个边是符合条件的最小权值,这个边加到MST里面,直到有V-1个边
lazy实现 时空复杂度:v个顶点,E条边 空间: E 时间: ElogE (最坏)
eager实现: 空间:v 时间ElogE(最坏)