第6章 图
6.1 概述
尽管在某种程度上,第5章所介绍的树结构也可用以表示一组对象间的二元关系,但仅限于父、子节点之间。
相互之间均可能存在二元关系的一组对象,从数据结构的角度分类,属于非线性结构(non-linear structure)。此类一般性的二元关系,属于图论(Graph Theory)的研究范畴。从算法的角度对此类结构的处理策略,与上一章相仿,也是通过遍历将其转化为半线性结构,进而借助树结构已有的处理方法和技巧,最终解决问题。
-
图:
定义为G = (V, E),其中,集合V中的元素称作顶点(vertex);集合E中的元素分别对应于V中的某一对顶点(u, v),表示它们之间存在某种关系,故亦称作边(edge)
从计算的需求出发,我们约定V和E均为有限集,通常将其规模分别记n = |V|和e = |E| 。
-
无向图、有向图及混合图:
无向边(undirected edge):边(u, v)所对应顶点u和v的次序无所谓。故无向边(u, v)也可记作(v, u)
有向边(directed edge):u和v不对等。约定有向边(u, v)从u指向v,u称作起点(origin)或尾顶点(tail),v称作终点(destination)或头顶点(head)
无向图(undirected graph,简称undigraph):E中各边均无方向
有向图(directed graph,简称digraph):E中只含有有向边
混合图(mixed graph):E中同时包含无向边和有向边
相对而言,有向图的通用性更强,因为无向图和混合图都可转化为有向图,如图6.1(c)所示,每条无向边(u, v)都可等效地替换为对称的一对有向边(u, v)和(v, u)。本章将主要针对有向图,介绍图结构及其算法的具体实现。
-
度:
在无向图中:与顶点v关联的边数,称作v的度数(degree),记作deg(v)
在有向图中:对于有向边e = (u, v),e称作u的出边(outgoing edge)、v的入边(incoming edge)。 v的出边总数称作其出度(out-degree),记作outdeg(v);入边总数称作其入度(in-degree),记作indeg(v)
-
简单图:
不含任何自环(self-loop)的图称作简单图(simple graph),也是本书主要讨论的对象。
-
通路与环路:
通路 简单通路 环路 简单环路 有向无环图 欧拉环路 汉密尔顿环路
-
带权网络
各边均带有权重的图,称作带权图(weighted graph)或带权网络(weighted network),有时也简称网络(network),记作G(V, E, wt())。
-
复杂度
问题的输入规模应以顶点数与边数的总和(n + e)来度量
无论顶点多少,边数都可能为0,即至少为0
那么在包含n个顶点的图中至多有多少条边?无向:n(n - 1)/2 有向:有n(n - 1) (利用组合数算的)
故总有 e =
6.2 抽象数据类型
操作接口:
Graph模板类:
0001 using VStatus = enum { UNDISCOVERED, DISCOVERED, VISITED }; //顶点状态
0002 using EType = enum { UNDETERMINED, TREE, CROSS, FORWARD, BACKWARD }; //边在遍历树中所属的类型
0003
0004 template <typename Tv, typename Te> //顶点类型、边类型
0005 class Graph { //图Graph模板类
0006 private:
0007 void reset() { //所有顶点、边的辅助信息复位
0008 for ( Rank v = 0; v < n; v++ ) { //所有顶点的
0009 status ( v ) = UNDISCOVERED; dTime ( v ) = fTime ( v ) = -1; //状态,时间标签
0010 parent ( v ) = -1; priority ( v ) = INT_MAX; //(在遍历树中的)父节点,优先级数
0011 for ( Rank u = 0; u < n; u++ ) //所有边的
0012 if ( exists ( v, u ) ) type ( v, u ) = UNDETERMINED; //类型
0013 }
0014 }
0015 void BFS ( Rank, int& ); //(连通域)广度优先搜索算法
0016 void DFS ( Rank, int& ); //(连通域)深度优先搜索算法
0017 void BCC ( Rank, int&, Stack<Rank>& ); //(连通域)基于DFS的双连通分量分解算法
0018 bool TSort ( Rank, int&, Stack<Tv>* ); //(连通域)基于DFS的拓扑排序算法
0019 template <typename PU> void PFS ( Rank, PU ); //(连通域)优先级搜索框架
0020 public:
0021 // 顶点
0022 int n; //顶点总数
0023 virtual Rank insert ( Tv const& ) = 0; //插入顶点,返回编号
0024 virtual Tv remove ( Rank ) = 0; //删除顶点及其关联边,返回该顶点信息
0025 virtual Tv& vertex ( Rank ) = 0; //顶点的数据(该顶点的确存在)
0026 virtual int inDegree ( Rank ) = 0; //顶点的入度(该顶点的确存在)
0027 virtual int outDegree ( Rank ) = 0; //顶点的出度(该顶点的确存在)
0028 virtual Rank firstNbr ( Rank ) = 0; //顶点的首个邻接顶点
0029 virtual Rank nextNbr ( Rank, Rank ) = 0; //顶点(相对当前邻居的)下一邻居
0030 virtual VStatus& status ( Rank ) = 0; //顶点的状态
0031 virtual int& dTime ( Rank ) = 0; //顶点的时间标签dTime
0032 virtual int& fTime ( Rank ) = 0; //顶点的时间标签fTime
0033 virtual Rank& parent ( Rank ) = 0; //顶点在遍历树中的父亲
0034 virtual int& priority ( Rank ) = 0; //顶点在遍历树中的优先级数
0035 // 边:这里约定,无向边均统一转化为方向互逆的一对有向边,从而将无向图视作有向图的特例
0036 int e; //边总数
0037 virtual bool exists ( Rank, Rank ) = 0; //边(v, u)是否存在
0038 virtual void insert ( Te const&, int, Rank, Rank ) = 0; //在两个顶点之间插入指定权重的边
0039 virtual Te remove ( Rank, Rank ) = 0; //删除一对顶点之间的边,返回该边信息
0040 virtual EType & type ( Rank, Rank ) = 0; //边的类型
0041 virtual Te& edge ( Rank, Rank ) = 0; //边的数据(该边的确存在)
0042 virtual int& weight ( Rank, Rank ) = 0; //边(v, u)的权重
0043 // 算法
0044 void bfs ( Rank ); //广度优先搜索算法
0045 void dfs ( Rank ); //深度优先搜索算法
0046 void bcc ( Rank ); //基于DFS的双连通分量分解算法
0047 Stack<Tv>* tSort ( Rank ); //基于DFS的拓扑排序算法
0048 void prim ( Rank ); //最小支撑树Prim算法
0049 void dijkstra ( Rank ); //最短路径Dijkstra算法
0050 template <typename PU> void pfs ( Rank, PU ); //优先级搜索框架
0051 };
“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 9 天,点击查看活动详情”