图
图的存储
邻接矩阵
邻接矩阵存储不带权图
无向图:若AB之间有边,则表中AB的值为1
**有向图:**若有A->B的边,则A行B列的值为1
#define MaxVertexNum 100
typedef struct{
char Vex[MaxVertexNum]; //顶点表
int Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵、边表 也可换成bool类型数据
int vexnum,arcnum; //图中顶点和边的个数
}MGraph;
-
无向图求顶点的度:若要求顶点A的度,则只需要遍历A行表中元素不为0的个数。第i个结点的度=第i行(第i列)的非零元素个数
-
有向图求顶点的入度出度:
若要求A的出度,则要遍历A行表中元素不为0的个数(第i个结点的出度=第i行非零元素的个数)
若要求入度,则要遍历A列表中元素不为0的个数(第i个结点的入度=第i列非零元素的个数)
第i个结点的度=入度+出度。
邻接矩阵存储带权图
#define MaxVertexNum 100
#define INFINITY 最大的int值 //宏定义无穷
typedef char VertexType; //顶点的数据类型
typedef int EdgeType; //带权图边上权值的类型
typedef struct{
VertexType Vex[MaxVertexNum];
EdgeType Edge[MaxVertexNum][MaxVertexNum];
int vexnum,arcnum;
}MGraph;
邻接矩阵法的性能分析
时间复杂度:O(n)
空间复杂度:O(n2)=O(|V|2) --只和顶点的个数有关,与实际的边数无关
⭐⭐⭐适合用于存储稠密图,稀疏图会浪费大量空间
无向图的邻接矩阵是对称矩阵,可以压缩存储(只存储上三角区/下三角区)
⭐邻接矩阵性质
设图G的邻接矩阵为A(矩阵元素为0/1),则An的元素Anij等于由顶点i到顶点j的长度为n的路径的数目。
邻接表
如图所示,使用邻接表法存储需要使用数组+链表的方式
#define MaxVertexNum 100
typedef struct ArcNode{ //边结点
int adjvex; //该弧所指向的顶点的位置
struct ArcNode *nextare; //指向下一条边的指针
// InfoType info; //边上的权值 网的边权值
}ArcNode;
typedef struct VNode{ //顶点表结点
VertexType data; //定点表的信息
ArcNode *firstarc //指向第一条依附该顶点的弧的指针
}VNode,AdjList[MaxVertexNum];
typedef struct{ //邻接表
AdjList vertices; //邻接表
int vexnum,arcnum; //顶点和边的个数
}
邻接表 邻接矩阵 空间复杂度 无向图 O ( ∣ V ∣ + 2 ∣ E ∣ ) ; 有向图 O ( ∣ V ∣ + ∣ E ∣ ) O ( ∣ V ∣ 2 ) 适用于 存储稀疏图 存储稠密图 表示方式 不唯一 唯一 计算出度 / 入度 / 度 计算有向图的度、入度不方便,其余很方便 必须遍历对应的行和列 找相邻的边 找有向图的入边不方便,其余很方便 必须遍历对应的行和列 \begin{array}{|c|c|c|} \hline &邻接表 &邻接矩阵\\ \hline 空间复杂度 & 无向图O(|V|+2|E|);有向图O(|V|+|E|) & O(|V|^2)\\ \hline 适用于 & 存储稀疏图 & 存储稠密图\\ \hline 表示方式 & 不唯一 & 唯一\\ \hline 计算出度/入度/度 & 计算有向图的度、入度不方便,其余很方便 &必须遍历对应的行和列\\ \hline 找相邻的边 &找有向图的入边不方便,其余很方便 &必须遍历对应的行和列\\ \hline \end{array} 空间复杂度适用于表示方式计算出度/入度/度找相邻的边邻接表无向图O(∣V∣+2∣E∣);有向图O(∣V∣+∣E∣)存储稀疏图不唯一计算有向图的度、入度不方便,其余很方便找有向图的入边不方便,其余很方便邻接矩阵O(∣V∣2)存储稠密图唯一必须遍历对应的行和列必须遍历对应的行和列
十字链表(存储有向图)
空间复杂度:O(|V|+|E|)
出边:顺着绿色的线路找
入边:顺着橙色的线路找
⭐Attention:只能存储有向图
#define MaxVertexNum 100
typedef struct ArcNode{ //边结点
int headvex; //弧头编号
int tailvex; //弧尾编号
struct ArcNode *hlink; //弧头相同的下一条弧
struct ArcNode *tlink; //弧尾相同的下一条弧
// InfoType info; //边上的权值 网的边权值
}ArcNode;
typedef struct VNode{ //顶点表结点
VertexType data; //顶点表的信息
ArcNode *firstin; //该顶点作为弧头的第一条弧
ArcNode *firstout; //该顶点作为弧尾的第一条弧
}VNode;
邻接多重表(存储无向图)
typedef struct ArcNode{ //边结点
int i,j; //边结点所连接的两个顶点的编号
struct ArcNode *ilink; //依附于顶点i的下条边
struct ArcNode *jlink; //依附于顶点j的下条边
//InfoType Info; // 权值
}ArcNode;
typedef struct VNode{
VertexType data; //顶点的数据
ArcNode *firstedge; //顶点所连接的第一条弧结点
}VNode, AdjList[MaxVertexNum];
typedef struct{ //邻接表
AdjList vertices; //邻接表
int vexnum,arcnum; //顶点和边的个数
}
空间复杂度:O(|V|+|E|)
⭐Attention:只能存储无向图
十字链表
邻接多重表
空间复杂度
O
(
∣
V
∣
+
∣
E
∣
)
O
(
∣
V
∣
+
∣
E
∣
)
找相邻边
很方便
很方便
删除边或者顶点
很方便
很方便
适用于
只能存有向图
只能存无向图
表示方式
不唯一
唯一
\begin{array}{|c|c|c|} \hline & 十字链表&邻接多重表\\ \hline 空间复杂度 &O(|V|+|E|)&O(|V|+|E|)\\ \hline 找相邻边 &很方便 &很方便\\ \hline 删除边或者顶点 &很方便&很方便\\ \hline 适用于 &只能存有向图 &只能存无向图\\ \hline 表示方式 &不唯一 &唯一\\ \hline \end{array}
空间复杂度找相邻边删除边或者顶点适用于表示方式十字链表O(∣V∣+∣E∣)很方便很方便只能存有向图不唯一邻接多重表O(∣V∣+∣E∣)很方便很方便只能存无向图唯一
图的遍历
广度优先遍历(BFS)⭐
算法要点
- 需要一个辅助队列
- 从一个结点找到与之邻接的其他结点
- visited数组防止重复访问
- 如何处理非连通图
算法实现
bool visited[MAX_VERTEX_NUM];
void BFSTraverse(Graph G){ //对图进行广度优先遍历
for(int i=0;i<G.vexnum;++i)
visiited[i]=false; //初始化visited表
InitQuene(Q); //初始化队列
for(int i=0;i<G.vexnum;++i) //可能存在图不连通的情况,若图不连通,则从一个结点出发并不能访问到所有结点,所以我们需要遍历visited表来找出尚未被访问的结点
if(!visited[i])
BFS(G,i);
}
void BFS(Graph G,int v){
visit(v);
visited(v)=true;
EnQuene(Q,v); //访问v结点,并将v结点入队
while(!QueneEmpty){
DeQuene(Q,v); //将v结点出队
for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)
//检测v所有邻接点
if(!visited[w]){
visit(w); //访问w
visited[w]=true; //将w设置为已访问
EnQuene(Q,w); //将w入队
}
}
}
算法分析
邻接矩阵存储的图的时间复杂度:
- 邻接矩阵存储的图:访问|V|个顶点需要O(|V|)的时间
- 查找每个顶点的邻接点都需要O(|V|)的时间
- 时间复杂度=O(|V|2)
邻接表存储的图的时间复杂度:
- 访问|V|个顶点需要O(|V|)的时间
- 查找各个顶点的邻接点共需要O(|E|)的时间
- 时间复杂度=O(|E|+|V|)
深度优先遍历(DFS)
算法要点
类似于树的先根遍历
- 空间开销主要来自于函数调用栈
- 从一个结点找到与之邻接的其他结点
- visited数组防止重复访问
- 如何处理非连通图
算法实现
bool visited[MAX_VERTEX_NUM]; //访问标记数组
void DFSTraverse(Graph G){
for(int v=0;v<G.vexnum;v++)
visited[v]=false; //初始化标记数组
for(int v=0;v<G.vexnum;v++)
if(!visited(v))
DFS(G,v);
}
void DFS(Graph G,int v){
visit(v); // 从顶点v出发,深度优先遍历图G
visited[v]=true; //访问顶点v
for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
if(!visited[w]) //w为u的尚未访问的邻接顶点
DFS(G,w);
}
算法分析
空间复杂度:O(|V|)(主要来自于函数递归调用栈)
时间复杂度:邻接表=O(|V|+|E|)
邻接矩阵=O(|V|2)