六度分割理论(小世界理论,六度空间理论):你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过五个中间人你就能够认识任何一个陌生人。
从陈家庄到张家村,怎么走最快呢?怎么修公路使得村村通的花费最少呢?
以上例子都是图在实际中的应用。
目录
2.1 网络(Activity On Vertex,AOV)
2.1 相邻矩阵(adjacency matrix,或邻接矩阵)
3.1 深度优先遍历(depth-first search,DFS)
3.2 广度优先遍历(breadth-first search,BFS)
5.1 单源最短路径(single-source shortest paths)
6 最小生成树(minimum-cost spanning tree, MST)
1 图的定义与术语
一种“多对多”的关系。
采用G= (V,E) 表示
—— V 是顶点 (vertex) 集合
—— E 是边 (edge) 的集合
1.1 无向图
(1)
无向图的边数:0≤e≤n(n-1)/2
(2)无向完全图
无向完全图(Undirected Complete Graph):恰有n(n-1)/2条边的无向图
1.2 有向图(directed graph)
(1) 有向图
有向图的边数:0≤e≤n(n-1)
(2)有向完全图
有向完全图(Directed Complete Graph):恰有n(n-1)条边的有向图
(3)有向树
有向树:有一个顶点的入度为0,其余顶点的入度均为1的有向图。
1.3 标号图(labeled graph)
1.4 带权图(weighted graph)
1.5 顶点的度
1.6 子图
1.7 回路 (cycle,也称为环)
回路:起点等于终点的路径
- n个顶点n条边的无向图一定是有环的。
- 判断有向图中是否存在回路,可以利用拓扑排序,还可以利用深度优先遍历
简单路径:路径序列中,顶点不重复出现的路径。最短路径一定是简单路径。
简单回路:除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路。
1.8 有根图(树,森林)
1.9 连通系列
(1)连通
对无向图 G= (V,E) 而言,如果从 V1 到 V2 有一条路径 (从 V2 到 V1 也一定有一条路径) ,则称 V1 和V2 是连通的 (connected)
若无向图G中,任意两个顶点都是连通的,则称图G为连通图。
若一个图有n个顶点,且边数小于n-1,此图必为非连通图。
(2)无向图连通分支=连通分量=极大连通子图
n个顶点的无向连通图,边数至少为n-1,至多为n(n-1)/2。
具有n个顶点的无向图,当有(n-1)(n-2) /2+1条边时能确保是一个连通图。
(3)生成树=极小连通子图
- 连通图的生成树:包含图中全部顶点的一个极小连通图。
- 若图中顶点数为n,则其生成树边数为n-1。
- 砍掉生成树一条边,就会变成非连通图。加上一条边就会形成一个回路。
- 具有n个顶点的图是一个环,去掉其中任意条边便是一棵生成树,则它有n棵生成树。
(4)有向图强连通
有n个顶点的强连通图最多有n(n-1)条边
2.0 顶点的度
(1)
(2)
(3)
想要交一个家里有矿的好gay友,这样就不用学数据结构了。
2.1 网络(Activity On Vertex,AOV)
2 图的存储结构
2.1 相邻矩阵(adjacency matrix,或邻接矩阵)
(1)有向图的相邻矩阵
(2)无向图的相邻矩阵
对于无向图的存储,可以省一半空间
2.2 邻接表( adjacency list )
(1)无向图的邻接表表示
边数*2 = 边表结点数(每条边在邻接表中存储两次)
(2)带权图的邻接表表示
(3)有向图的邻接表(出边表)
边数 = 边表结点数
(4)有向图的逆邻接表(入边表)
2.3 十字链表 (Orthogonal List)
十字链表 (Orthogonal List) 可以看成是邻接表和逆邻接表的结合
3 图的遍历(graph traversal)
给出一个图G和其中任意一个顶点V0,从V0出发系统地访问G中所有的顶点,每个顶点访问而且只访问一次
会遇到的问题:
(1)从一顶点出发,可能不能到达所有其它的顶点
如:非连通图
(2)也有可能会陷入死循环
如:存在回路的图
解决办法:
(1)为每个顶点保留一个 标志位 (mark bit)
(2)算法开始时,所有顶点的标志位置零
(3)在遍历的过程中,当某个顶点被访问时,其标志位就被标记为已访问
3.1 深度优先遍历(depth-first search,DFS)
类似于树的先序遍历

3.2 广度优先遍历(breadth-first search,BFS)
类似于树的层序遍历
总结
- 图的广度优先生成树的树高小或等于深度优先生成树的树高
4 拓扑排序(topological sort)
4.1 基本定义
如果图中从V到W有一条有向路径,则V一定排在W之前。满足此条件的顶点序列称为一个拓扑序列
获得一个拓扑序的过程就是拓扑排序
AOV如果有合理的拓扑序,则必定是有向无环图(Directed Acyclic Graph, DAG)
网络(AOV)为带权的连通图
栗子1:
栗子2
栗子3
拓扑排序的时间复杂度
4.2 关键路径-AOE网
拓扑排序主要用于解决一个工程能否顺利进行,但有时我们还需解决工程完成所需要的最短时间问题。好比求汽车厂造一辆车,最短需要多少时间?
AOE网(Activity On Edge Network):每条边代表一个活动。
栗子1:
4要等1,2,5都完工了才能进行。
整个工期为18天。
机动时间(从后往前推):找出哪些组是不用赶工的。
栗子2:
路径长度:路径上各个活动所持续的时间之和。
关键路径:从源点到汇点具有最大长度的路径。绝对不允许延误的活动组成的路径。没有机动时间的组组成的路径。
—— 图中,开始 - 发动机完成 - 部件集中到位 - 组装完成就是关键路径。
—— 路径长度为5.5
关键活动:关键路径上的活动。
5 最短路径
5.1 单源最短路径(single-source shortest paths)
从某固定源点出发,求其到所有其他顶点的最短路径。
5.1.1 Dijkstra算法
(1)
毋庸置疑,顶点v0到v1的最短路径为1
由于顶点v1还与v2,v3,v4连线,因此可同时求得
v0->v1->v2=1+3=4
v0->v1->v3=1+7=8
v0->v1->v4=1+5=6
此时,v0到v2的最短路径为4而非5。
(2)
由于顶点v2还与v4,v5连线,因此可求得
v0->v1->v2->v4=5要比v0->v2->v4=5+1=6小
所以v0到v4的最小路径为5。
其他同理。过程都是基于已经求出的最短路劲的基础上,求得更远顶点的最短路径。
总结
Dijkstra不支持负权值
代码实现
class Dist{//用于保存最短路径信息
public:
int index;//结点的索引值,仅Dijkstra算法用到
int length;//当前最短路径长度
int pre;//路径最后经过的点
};
void dijkstra(Graph &G, int s, Dist &D){//s是源点
D = new Dist[G.verticesNum()];//记录当前最短路径长度
for(int i = 0; i < G.verticesNum(); i++){//初始化
G.Mark[i] = UNVISTED;
D[i].index = i;
D[i].length = INFINITE;//无穷的
D[i].pre = s;
}
D[s].length = 0;//源点到自身的路径长度置为0
MinHeap<Dist> H(G.edgesNum());//最小值堆用于找出最短路径
H.insert(D[s]);
for(i=0; i < G.verticesNum(); i++){
bool FOUND = false;
Dist d;
while(!H.isEmpty()){
d = H.removeMin();//获得到s路径长度最小的结点
if(G.Mark[d.index] == UNVISITED){//如果未访问过就跳出循环
FOUND = true;break;
}
}
if(!FOUND) break;//若没有符合条件的最短路径则跳出本次循环
int v = d.index;
G.Mark[v] = VISITED;//若标记位设置为VISITED
for(Edge e = G.firstEdge(v); G.isEdge(e); e=G.nextEdge(e)){//刷新最短路
if (D[G.toVertex(e)].length > (D[v].length+G.weight(e))) {
D[G.toVertex(e)].length = D[v].length + G.Weight(e);
D[G.toVertex(e)].pre = v;
H.insert(D[G.toVertex(e)]);
}
}
}
}
5.2 每对结点间的最短路径
求任意两顶点间的最短路径
5.2.1 Floyd算法
基本思想
Floyd算法定义了两个二维矩阵:
(1) 矩阵D记录顶点间的最小路径
例如D[0][3]= 10,说明顶点0 到 3 的最短路径为10;
(2) 矩阵P记录顶点间最小路径中的中转点
例如P[0][3]= 1 说明,0 到 3的最短路径轨迹为:0 -> 1 -> 3。(1为中转点)它通过3重循环,k为中转点,v为起点,w为终点,循环比较D[v][w] 和 D[v][k] + D[k][w] 最小值,如果D[v][k] + D[k][w] 为更小值,则把D[v][k] + D[k][w] 覆盖保存在D[v][w]中。
总结
Floyd支持负权值
6 最小生成树(minimum-cost spanning tree, MST)
6.1 贪心算法
6.2 Prim 算法
6.3 Kruskal 算法
(1)
(2)
(3)
(4)
总结
- 最小生成树的树形可能不唯一,但代价一定是唯一的。
- 树的代价 = 树的全部边权值相加
- 无向连通图必然存在最小生成树。当无向连通图存在权值相同的多条边,最小生成树不一定唯一。所以,任何一个无向连通图的最小生成树有一棵或者多棵。
攻略结束,各位可以开始上吊了。
本文参考自:
张铭《数据结构与算法》
陈越,何钦铭《数据结构》
程杰《大话数据结构》