数据结构图之上吊攻略

六度分割理论(小世界理论,六度空间理论):你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过五个中间人你就能够认识任何一个陌生人。

从陈家庄到张家村,怎么走最快呢?怎么修公路使得村村通的花费最少呢?

 

以上例子都是在实际中的应用。

目录

1 图的定义与术语

1.1 无向图

1.2 有向图(directed graph)

(1) 有向图

(2)有向完全图

(3)有向树

1.3 标号图(labeled graph)

1.4 带权图(weighted graph)

1.5 顶点的度

1.6 子图

1.7 回路 (cycle,也称为环)

1.8 有根图(树,森林)

1.9 连通系列

(1)连通

(2)无向图连通分支=连通分量=极大连通子图

(3)生成树=极小连通子图

(4)有向图强连通

2.0 顶点的度

2.1 网络(Activity On Vertex,AOV)

2 图的存储结构

2.1 相邻矩阵(adjacency matrix,或邻接矩阵)

(1)有向图的相邻矩阵

(2)无向图的相邻矩阵

2.2 邻接表( adjacency list )

(1)无向图的邻接表表示

(2)带权图的邻接表表示

(3)有向图的邻接表(出边表)

(4)有向图的逆邻接表(入边表)

2.3 十字链表 (Orthogonal List)

3 图的遍历(graph traversal)

3.1 深度优先遍历(depth-first search,DFS)

​3.2 广度优先遍历(breadth-first search,BFS)

​4 拓扑排序(topological sort)

4.1 基本定义

4.2 关键路径-AOE网

5 最短路径

5.1 单源最短路径(single-source shortest paths)

5.1.1 Dijkstra算法

5.2 每对结点间的最短路径

5.2.1 Floyd算法

6 最小生成树(minimum-cost spanning tree, MST)

6.1 贪心算法

6.2 Prim 算法

6.3 Kruskal 算法


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)条边

最少有n条边 

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)

总结

  • 最小生成树的树形可能不唯一,但代价一定是唯一的。
  • 树的代价 = 树的全部边权值相加
  • 无向连通图必然存在最小生成树。当无向连通图存在权值相同的多条边最小生成树不一定唯一。所以,任何一个无向连通图的最小生成树有一棵或者多棵。

攻略结束,各位可以开始上吊了。


本文参考自:

张铭《数据结构与算法》

陈越,何钦铭《数据结构》

程杰《大话数据结构》

《弗洛伊德(Floyd)算法求图的最短路径》

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值