术语
完全图
任何 两个顶点都有边数量关系
n表示顶点数量,e表示边的数量,
无向图中的e为[0, n(n-1)/2]
有向图中的e为[0, n(n-1)/2]
图中所有的顶点度数之和(出度+入度)为2*e简单路径、简单回路
一条路径中,除了起点和终点外,若其余顶点各不相同,则称改路径为简单路径;由简单路径构成的回路成为简单回路
连通图、强连通图
无向图中,如果任意两个顶点都可以互相到达,则称为连通,图中的任意两点都连通为连通图,其极大连通子图成为连通分量
有向图中,如果任意两个顶点都有路径,则称为强连通,图中任意两个点都强连通则为强连通图,其极大连通子图成为强连通分量生成树
一个含有n个顶点的的连通图的生成树是一个极小连通子图,它含有图中的全部顶点,,但有且只有足以构成一棵树的n-1条边
生成树上添加1条边必定构成一个环
有n-1条边的不一定是生成树
一个图的生成树不唯一AOV网:结点表示活动的网;
- AOE网:边表示活动的持续时间的网;
图的表示
- 邻接矩阵
无向图一定是对称的,有向图不一定对称
int [][]二维数组 - 邻接表
List<List<Node>>
图的遍历
DFS
12345678910111213141516DFS(u){//访问顶点uvis[u] = true;for(u能到达的所有顶点v){if(vis[v] == false){DFS[v];}}}DFSTrave(G){//遍历图Gfor(G的所有顶点u){if(vis[u] == false){DFS(u);}}}BFS
1234567891011121314151617181920BFS(u){while(q 不为空){//q为队列,LinkedListu = q.remove();//取出头结点for(u出发可以到达的所有顶点v){if(inq(v) == false){//没有入过队列q.add(v);//v入队inq[v] = true;//已经入队列}}}}BFSTrave(G){for(G的所有顶点u){if(inq[u] == false){Queue<> q = new LinkedList<>();//产生队列BFS(u);//访问inq[u] = true;//标记进队}}}
最短路径
- 单源最短路径Dij123456789101112131415161718192021//d为源点到每个点的最短距离记录,s为起点//pre为最短路径的记录顺序,pre[v] = u表示v的前驱结点是uDij(G, d[], s,pre[]){初始化;for(循环n次){u= 使得d[u]最小且没有被访问的节点;//这里可以用priorityQueue等标记u访问过;for(u 能到达的所有点v){if(v未访问 && 以u为中介点使得s到v的距离更短){优化d[v];pre[v] = u;//记录最短路径}/*else(距离相等){其他标尺}*/}}}
Dij+DFS:很可能在求最短路径的时候不止有一条,因此最短路径得到顺序pre后,还可以用dfs得到权值最小的路径
- 全源最短路径Floyd
3层遍历,枚举节点k123456789for(int k = 0; k < n; k++){for(int i = 0; i < n; i++){for(int j = 0; j < n; j++){if(d[i][k] + d[k][j] < d[i][j]){d[i][j] = d[i][k] + d[k][j];}}}}
最小生成树MST
性质
1.只在无向图中有
2.最小生成树是树,边数 = 点数 - 1,且一定无环
3.最小生成树不一定唯一,但是其边权之和一定唯一Prim
算法思想和Dij相似,但是Prim是一个集合的最小距离,而Dij到一个点的最小距离12345678910111213141516//d为源点到每个点的最短距离记录,s为起点//pre为最短路径的记录顺序,pre[v] = u表示v的前驱结点是uint Prim(G, d[], s){int ans = 0;//初始化for(循环n次){u= 使得d[u]最小且没有被访问的节点;//这里可以用priorityQueue等标记u访问过;ans += d[u];for(u 能到达的所有点v){if(v未访问 && 以u为中介点使得已选中的集合到v的距离更短){优化d[v];}}}return ans;}Kruskal
对边排序,然后选择最小的,合并到一起
使用了并查集和排序12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182public class Kruskal {//点数int numv = 100;ArrayList<Edge> edges = new ArrayList<>();int father[] = new int[numv];//并查集public int findfather(int x){int a = x;while(x != father[x]){x = father[x];}//路径压缩,放置树太高,影响查找效率while(a != father[a]){int z = a;a = father[a];father[z] = a;}return x;}public int Kru(int v, int e){//最小生成树的权值int ans = 0;//生成树的边数int vis = 0;//初始化并查集for (int i = 0; i < v; i++) {father[i] = i;}//对边的权值进行排序Collections.sort(edges);for(int i = 0; i < e; i++){int uu = edges.get(i).u;int vv = edges.get(i).v;//查看vv和uu是不是在同一个集合中int fau = findfather(uu);int fav = findfather(vv);if(fau != fav){//加入到集合中去father[fau] = fav;//生成树权值增加ans += edges.get(i).cost;//生成树边数增加vis++;//生成树的边数为n-1,结束生成树查找if(vis == v - 1){break;}}}if(vis == v - 1){//返回生成树权值return ans;}else{//不能生成生成树return -1;}}}class Edge implements Comparable<Edge>{int u,v;int cost;@Overridepublic int compareTo(Edge o) {return Integer.compare(o.cost, this.cost);}}
拓扑序列
判断一个图是不是有向无环图
准备所有顶点的入度数组
1、定义一个队列,把所有入度为0的点加入队列中
2、取出队首,对其所有相邻的边进行操作:将该边到达的点的入度减1,如果该点的入度为0,则加入到队列,删除这条边
3、重复执行2直到队列为空,判断遍历过的点的数目是不是等于该图的节点数