(CSDN的markdown是不是有问题啊……代码行总会读取上面的尾巴ε=ε=ε=(#>д<)ノ
关于图的若干定义
(具体参考离散数学......)
G=(V,E)
其中G为图,V为顶点集(vertex),E为边集(edge)
每一条边就是一个点对(v,w),其中v,w∈V
当点对有序时,w和v称为有向的,这样的图称为有向图
当v与w之间存在边时,我们称顶点v与w邻接。
有时边存在着称为权(weight)或值(cost)的属性
邻接表
邻接矩阵(adjacency matrix)
- 对于每条边(u,v),我们置A[u][v]=1,否则为0。该方法用于表示稠密(dense)的图,但并不是很有效,空间上的消耗太大了。空间需求为O(|v|^2)
邻接表(adjacency list)
- 对每一个顶点,使用一个表存放所有邻接的顶点,此时的空间需求为O(|E|+|V|)
- 具体实现:
- 对每个节点,创建相邻节点的链表
- 对于有向图,每条边有一个入口
- 对于正则图,每条边有两个入口【待考】
- 散列表实现:
- 每个节点都存储一个名字以及一个内部编号
- 制定一个编号,f(0)= 1,读入该图
- 读入每条边,检查是否两个顶点在散列表中,如果在,则使用该内部编号,如果不在,将下一个可用的编号分配给该顶点并将其插入到散列表中。分配好的内部编号就是散列函数最终的结果。
拓扑排序(Topological sort)
- 拓扑排序是对有向无圈图的顶点的一种排序,它使得如果存在一条vi到vj的路径,那么在排序中vj在vi的后面。
- 拓扑排序不是唯一的。
- 算法概述:
- 找出任意一个入度为0的顶点
- 输出该顶点
- 把该顶点与它的边从图中删除
- 重复以上步骤直至输出完毕
邻接表实现伪代码描述:
void Topsort(Graph G) { int Counter; Vertex V,W; for(Counter=0;Counter<numVertex;Counter++) { V=FindNewVertexOfIndgreeZero();//找到一个入度为0的点,如果这样的点不存在,返回NotAVertex if(V==NotAVertex) { Error("Graph has a cycle"); break; } TopNum[V]=Counter;//大概是将V的编号赋为Counter(顺序值)【待考】 foreach W adjacent to V Indegree[W]--;//由于V被删除所以所有前置节点为V的节点,其入度应当减一 } }
队列实现伪代码描述
最短路径算法
深度优先搜索
广度优先搜索(breadth-first search)
无权最短路径
- 使用某个顶点s作为输入参数,我们想找到从s到所有其他顶点的最短路径。
- 选择某点作为s点,并将其标记为0
- 寻找与s点距离为1的点,将其标记为1
- …
- 最远的顶点最后被标记
- 这个方法有点像是对树的层序遍历
算法概述:我们利用一个存储三种不同信息的表来进行遍历。其中:
- dv用于存储从s开始到顶点的距离
- pv用于存储实际的路径(前一节点(的索引))
- 布尔值Known用于标记节点是否已经处理完成(所找到的路径长为最短)
- 伪代码实现:
该种方法的缺点在于对于最坏情形时执行的很低效。下面给出改进后的伪代码表示:
队列具有FIFO性,所以入队出队的顺序就是拓扑编号顺序。
Dijkstra算法
算法概述:在每个阶段,Dijkstra算法选择一个顶点v,它在所有未知定点中具有最小的dv,同时算法声明从s到v的最短路径是已知的,阶段的其他部分由dw值的更新工作完成。
- dv用于存储使用已知顶点作为中间顶点从s开始到v的最短路径的长。
- pv用于存储引起dv变化的最后的顶点。
- 布尔值Known用于标记节点是否已经处理完成(所找到的路径长为最短)
伪代码实现:
- 负边图
- 无圈图
最小生成树
- 最小生成树:连接图G的所有顶点的边所构成的树,且其总价值最低。
- 两种算法的差距:是否需要在执行过程中保持图的连通性。
- Prim算法:
- 每一步都把一个节点当做根并向上加边
- 在每一阶段都可以通过选择边(u , v),使得(u,v)的值是所有u在树上但v不在树上的边的值中的最小者
- Kruskal算法:
- 连续的按照最小的权选择边,并且当所选的边不产生圈的时候就把它作为选定的边。