diff --git a/Code/Code.vcxproj b/Code/Code.vcxproj index ec632d7..97588d6 100644 --- a/Code/Code.vcxproj +++ b/Code/Code.vcxproj @@ -143,11 +143,11 @@ - + diff --git a/Code/graph.h b/Code/graph.h new file mode 100644 index 0000000..d42da18 --- /dev/null +++ b/Code/graph.h @@ -0,0 +1,27 @@ +#include +#include +#include "head.h" + +// ÁڽӾØÕóͼ +typedef struct { + // ¶¥µã±í + element_type vex[MAXSIZE]; + // ÁڽӾØÕ󣬱߱í + int edge[MAXSIZE][MAXSIZE]; + // ͼµ±ǰµĶ¥µãÊýºͱßÊý/»¡Êý + int vex_num, arc_num; +} AdjacencyMatrixGraph; + +// Áڽӱí½áµã +typedef struct AdjacencyListNode { + // ¶¥µãÐÅϢ + element_type vex; + // µÚһÌõ±ß + AdjacencyListNode* first; +} AdjacencyListNode, AdjacencyList[MAXSIZE]; + +// Áڽӱí +typedef struct { + AdjacencyList data; + int vex_num, arc_num; +} AdjacencyListGraph; diff --git a/Code/head.h b/Code/head.h index 721224c..763ee9b 100644 --- a/Code/head.h +++ b/Code/head.h @@ -1,4 +1,5 @@ // ³õʼ»¯×î´󳤶È #define MAXSIZE 25 #define DEFAULTELEM 0 +#define INFINITY 32767 typedef char element_type; diff --git a/Data-Structrue/graph.md b/Data-Structrue/graph.md index 47e54ff..7c098d7 100644 --- a/Data-Structrue/graph.md +++ b/Data-Structrue/graph.md @@ -12,21 +12,16 @@ ### 图的类别 -#### 无向图 - -若E是无向边(简称边)的有限集合时,则图G为无向图。边是顶点的无序对,记为(v, w)或(w,v),因为(v, w)=(w,v),其中v、w是顶点。可以说顶点w和顶点v互为邻接点。边(v, w)依附于顶点w和v,或者说边(v, w)和顶点v、w相关联。 - -#### 有向图 - -若E是有向边(也称弧)的有限集合时,则图G为有向图。弧是顶点的有序对,记为,其中v、w是顶点,v称为弧尾,w称为弧头,称为从顶点v到顶点w的弧,也称v邻接到w,或w邻接自v。≠。 - -#### 简单图 - -不存在重复边,且不存在顶点到自身的边。一般的图默认是简单图。 - -#### 多重图 - -图G中某两个结点之间的边数多于一条,又允许顶点通过同一条边与自己关联。 ++ 无向图:若E是无向边(简称边)的有限集合时,则图G为无向图。边是顶点的无序对,记为(v, w)或(w,v),因为(v, w)=(w,v),其中v、w是顶点。可以说顶点w和顶点v互为邻接点。边(v, w)依附于顶点w和v,或者说边(v, w)和顶点v、w相关联。 ++ 有向图:若E是有向边(也称弧)的有限集合时,则图G为有向图。弧是顶点的有序对,记为,其中v、w是顶点,v称为弧尾,w称为弧头,称为从顶点v到顶点w的弧,也称v邻接到w,或w邻接自v。≠。 ++ 简单图:不存在重复边,且不存在顶点到自身的边。一般的图默认是简单图。 ++ 多重图:图G中某两个结点之间的边数多于一条,又允许顶点通过同一条边与自己关联。 ++ 无向完全图:无向图中任意两个顶点之间都存在边。 ++ 有向完全图:有向图中任意两个顶点之间都存在方向相反的两条弧。 ++ 稀疏图:一般$\vert E\vert<\vert V\vert\log\vert V\vert$的图。 ++ 稠密图:一般$\vert E\vert>\vert V\vert\log\vert V\vert$的图。 ++ 树:不存在回路,且连通的无向图。 ++ 有向树:一个顶点的入度为0,其余顶点入度均为1的有向图。 ### 顶点的度 @@ -37,4 +32,205 @@ ### 顶点的关系 + 路径:从一个点到另一个点所经过的顶点序列。 -+ 回路(环):第一个顶点与最后一个顶点相同的路径。 \ No newline at end of file ++ 回路(环):第一个顶点与最后一个顶点相同的路径。 ++ 长度(无权图):沿路径所经过的边数成为该路径的长度。 ++ 简单路径:路径中的顶点不重复出现。 ++ 简单回路:由简单路径组成的回路。 ++ 点到点的距离:从顶点u到顶点v的最短路径若存在,则此路径的长度就是从u到v的路径,若不存在路径,则记该路径为无穷。 ++ 连通:在无向图中,若从顶点v到顶点u有路径存在,则称uv是连通的。 ++ 强连通:在有向图中,若从顶点v到顶点u和从顶点u到顶点v之间都有路径,则称强连通。 + +### 图的连通 + ++ 连通图:无向图中任意两个顶点之间都是连通的。对于n个顶点的无向图,若其是连通图,则最少需要n-1条边,若其是非连通图,则最多有$C_{n-1}^2$条边。 ++ 强连通图:有向图中任意两个顶点之间都是强连通的。对于n个顶点的有相同,若其是强连通图,则最少需要n条边来形成环路。 ++ 子图:设有两个图G=(V,E)和G'=(V',E'),若V'是V的子集,E'是E的子集,则G'是G的子图。 ++ 生成子图:若有满足V(G')=V(G)的子图G',则G'是G的生成子图。 ++ 连通分量:无向图G中的极大连通子图称为G的连通分量;对任何连通图而言,连通分量就是其自身。 ++ 强连通分量:有向图G中的极大连通子图称为G的强连通分量;对任何强连通图而言,强连通分量就是其自身。 ++ 生成树:包含连通图中全部顶点的一个极小连通子图。若图的顶点为n,则其生成树包含n-1条边,若去掉生成树的一条边则会变成非连通图,若加上一条边则会形成一个回路。 ++ 生成森林:在非连通图中,连通分量的生成树构成了非连通图的生成森林。 + +### 图的权 + ++ 边的权:在一个图中,每条边都可以表上具有某种含义的数值,这就是该边的权值。 ++ 网络(网):若图中的每条边都有权,这个带权图被称为网。 ++ 带权路径长度:取沿路径各边的权之和作为此路径的长度。 + +#### 无权图 + +若v[i][j]=0,表示$v_{i+1}$到$v_{j+1}$是不连通的,若v[i][j]=1,表示$v_{i+1}$到$v_{j+1}$是连通的。 + +#### 网 + +若v[i][j]=∞,表示$v_{i+1}$到$v_{j+1}$是不连通的,若v[i][j]=某权值,表示$v_{i+1}$到$v_{j+1}$是连通的。 + +## 图的存储结构 + +### 邻接矩阵 + +使用一个长宽皆为$\vert v\vert$的二维矩阵v,从左上角到右上角,从左上角到左下角,分别标识表示$v_1,v_2\cdots,v_n$。假设矩阵的索引从0开始,而结点编号从1开始。 + ++ 对于无向图,第i个结点的度=第i行或第i列的非零元素个数。 ++ 对于有向图,第i个结点的入度=第i行的非零元素个数;第i个结点的出度=第i列的非零元素个数;第i个结点的度=第i行的非零元素个数+第i列的非零元素个数。 + +空间复杂度是$O(\vert V\vert^2)$。 + +适用于存储稠密图。 + +对于无向图,因为没有方向,所以只有两点连接就是连通的,从而无向图的邻接矩阵都是主对角线对称的。因为对称,所以可以压缩存储。 + +设图G的邻接矩阵为$A$,矩阵元素为0或1,则$A^n$的元素$A^n[i][j]$表示由顶点$v_{i+1}$到结点$v_{j+1}$的长度为n的路径的数目。 + +### 邻接表 + +邻接表存储方式是顺序存储与链式存储的结合,存储方式和树的孩子表示法类似。 + +使用一个数组保存图的每一个结点,每一个结点元素包含一个指向后一条边的指针。 + +对于无向图,因为同一条边两端的点会重复存储,所以空间复杂度为$O(\vert V\vert+2\vert E\vert)$,而对于有向图空间复杂度为$O(\vert V\vert+\vert E\vert)$。 + ++ 对于无向图,每个结点的边链表的结点数就是该结点的度。 ++ 对于有向图,每个结点的边链表的结点数就是该结点的出度,而对于入度就只能遍历所有结点的结点链表。 + +邻接表的表示方式是不唯一的。 + +### 十字链表 + +十字链表置用于存储有向图。可以解决邻接矩阵空间复杂度高和邻接表计算入度入边不方便的问题。 + +十字链表定义了两种结点: + ++ 顶点结点:用于表示顶点,被一个数组包裹。 + + 数据域。 + + 该顶点作为弧头的第一条弧。 + + 该顶点作为弧尾的第一条弧。 ++ 弧结点:被顶点结点指向的结点。 + + 弧尾顶点编号。 + + 弧头顶点编号。 + + 权值。 + + 弧头相同的下一条弧。 + + 弧尾相同的下一条弧。 + +空间复杂度为$O(\vert V\vert+\vert E\vert)$。 + +### 邻接多重表 + +邻接多重表用于存储无向图,可以解决邻接矩阵空间复杂度高和邻接表删除插入结点不方便的问题。 + +邻接多重表定义了两种结点: + ++ 顶点结点:用于表示顶点,被一个数组包裹。 + + 数据域。 + + 该顶点相连的第一条边。 ++ 边结点:被顶点结点指向的结点。 + + 边一端编号i。 + + 边另一端编号j。 + + 权值。 + + 依附于i的下一条边。 + + 依附于j的下一条边。 + +空间复杂度为$O(\vert V\vert+\vert E\vert)$。 + +## 图的基本操作 + +### 图查找 + +#### 查找边 + +使用邻接矩阵只用根据对应行列的元素是否为1或某值就可以了,如果是0或无穷,就代表没有该邻边。时间复杂度为$O(1)$。 + +而使用邻接矩阵需要从一端点出发遍历对应的结点链表,如果能在链表中找到另一端点的索引,就代表有边。时间复杂度为$O(1)$到$O(\vert V\vert)$。 + +#### 查找点邻边 + +对于无向图,邻接矩阵需要遍历对应结点的那一行,所有数值为1或某数值的列就是对应的有边的另一个端点。时间复杂度为$O(\vert V\vert)$。 + +对于有向图,邻接矩阵需要遍历对应结点的那一行得到出边以及那一列代表入边,所有数值为1或某数值的列就是对应的有边的另一个端点。时间复杂度为$O(\vert V\vert)$。 + +对于无向图,邻接表只用遍历对应结点的结点链表就可以。时间复杂度为$O(1)$到$O(\vert V\vert)$。 + +对于有向图,邻接表用遍历对应结点的结点链表得到出边,而对于入边需要遍历所有邻接表的边结点。出边时间复杂度为$O(1)$到$O(\vert V\vert)$,入边时间复杂度为$O(\vert E\vert)$。 + +#### 查找头邻接点 + +邻接矩阵只用扫描对应的行,找到结点就可以了。时间复杂度为$O(1)$到$O(\vert V\vert)$。 + +对于无向图,邻接表只用找到结点的边结点的第一个结点。时间复杂度为$O(1)$。 + +对于有向图,出边邻接表只用找到结点的边结点的第一个结点。时间复杂度为$O(1)$。而对于入边需要遍历所有的结点的第一个链表结点。时间复杂度为$O(1)$到$O(\vert E\vert)$。 + +#### 查找下一个邻接点 + +邻接矩阵只用扫描对应的行,找到结点就可以了。时间复杂度为$O(1)$到$O(\vert V\vert)$。 + +邻接表只用找到当前结点的下一个结点。时间复杂度为$O(1)$。 + +### 图插入 + +邻接矩阵只用在最后增加一行一列。时间复杂度是$O(1)$。 + +邻接表只用在存储结点的数组的末尾添加一个结点,指针设置为NULL。时间复杂度是$O(1)$。 + +### 图删除 + +邻接矩阵的删除元素分为两种方式,如果是直接删除对应元素行与列上的所有元素并移动其他元素,那么时间复杂度就是$O(\vert V\vert^2)$,如果删除对应元素行与列上的所有元素但是不移动其他元素,而是将保存结点数据的数组中对应结点的数据变为NULL,则时间复杂度就是$O(\vert V\vert)$。 + +对于无向图,邻接表的删除需要删除该结点并删除结点后连接的所有结点链表元素,时间复杂度为$O(1)$到$O(\vert V\vert)$。 + +对于有向图,邻接表的删除需要删除该结点并删除结点后连接的所有结点链表元素且还要遍历所有的边并删除,删除出边时间复杂度为$O(1)$到$O(\vert V\vert)$,删除入边时间复杂度为$O(\vert E\vert)$。 + +### 图遍历 + +分为广度优先BFS与深度优先DFS。 + +#### 广度优先遍历 + ++ 找到一个与顶点相邻的所有结点。 ++ 标记哪些结点被访问过。 ++ 需要一个辅助队列保存结点是否被访问的数据。 + +广度优先遍历过程: + ++ 访问顶点v。 ++ 访问v的所有未被访问的邻接点。 ++ 依次从这些邻接点(在步骤②中访问的顶点)出发,访问它们的所有未被访问的邻接点; 依此类推,直到图中所有访问过的顶点的邻接点都被访问。 + +邻接矩阵实现时的时间复杂度为$O(\vert V\vert^2)$,邻接表实现时的时间复杂度为$O(\vert V\vert+\vert E\vert)$;空间复杂度为$O(\vert V\vert)$。 + +广度优先生成树:根据广度优先遍历可以将所有第一次访问结点时的路径组合生成一个广度优先生成树,若图结点为n个,则生成树边一共有n-1条。因为保存图的数据结构若是不唯一,则其广度优先生成树也是不唯一的。 +广度优先生成森林:若图是不连通的,那会生成连通分量个广度优先生成树,就构成了广度优先生成森林。 + +#### 深度优先遍历 + +类似于树的先根遍历。 + +深度优先遍历过程: + ++ 访问顶点v。 ++ 依次从v的未被访问的邻接点出发,对图进行深度优先遍历。 ++ 直至图中和v有路径相通的顶点都被访问。 ++ 若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。 + +邻接矩阵实现时的时间复杂度为$O(\vert V\vert^2)$,邻接表实现时的时间复杂度为$O(\vert V\vert+\vert E\vert)$;空间复杂度为$O(\vert V\vert)$。 + +邻接表的深度优先序列会优先选择每个结点的第一个相邻结点,即结点链中的第一个元素。 + +邻接矩阵方式唯一所以深度优先序列唯一,而邻接表方式不唯一,所以深度优先序列不唯一。 + +深度优先生成树:根据深度优先遍历可以将所有第一次访问结点时的路径组合生成一个深度优先生成树,若图结点为n个,则生成树边一共有n-1条。因为保存图的数据结构若是不唯一,则其深度优先生成树也是不唯一的。 +深度优先生成森林:若图是不连通的,那会生成连通分量个深度优先生成树,就构成了深度优先生成森林。 + +#### 图遍历与图连通性 + +若起始顶点到其他各顶点都有路径,那么只需调用一次深度优先或广度优先遍历函数。 + +对强连通图,从任意一结点出发都只用调用一次深度优先或广度优先遍历函数。 + +## 图的应用 + +### 最小生成树 + +#### 最小生成树定义 + +最小生成树也是最小代价树。 \ No newline at end of file