该文章仅为本人考研复习之用,根据记忆整理,如有错误,请多指正。
文章目录
1.基础概念
①完全图
②
2.存储及操作
图的存储包含四种存储结构:
2.1邻接矩阵
顺序存储-适合稠密图
#define maxvex 100
typedef char vextype; //结点表类型
typedef int arctype; //边表类型
typedef struct Graph{ //邻接矩阵存储法
vextype vexlist[maxvex]; //顶点表
arctype arclist[maxvex][maxvex];//邻接矩阵
}Graph;
特点:
①对称矩阵-某图的边个数 = 邻接矩阵中所有1元素/2;
②若图为有向图,则第i行元素1的个数是顶点i的出度,第i列元素1的个数是顶点的入度;
③若邻接矩阵A,则A的n次方[i][j]代表从结点i到结点j长度为n的路径条数
2.2邻接表
顺序存储和链式存储结合-适合稀疏图
#define maxvex 100
typedef char vextype;
typedef int arctype;
//边结点
typedef struct arcnode{
arctype vexloc;
struct arcnode *nextarc;
}arcnode;
//顶点结点
typedef struct vexnode{
vextype data;
struct arcnode *firstarc;
}vexnode,vexlist[maxvex];
//邻接表
typedef struct Graph{
vexlist vertices;
int vexnum,arcnum;
}Graph;
2.3十字链表
有向图的链式存储结构
2.4邻接多重表
无向图的链式存储结构
2.5存储-总结
在上述的四种存储方式中,邻接矩阵和邻接表法存储的边数是现有图结构中边数的两倍,而十字链表法和邻接多重表法存储的边数和图结构中边数相等的。
3.图的遍历
3.1广度优先遍历(BFS)
树是图的一种特殊形式,图的广度优先遍历原理类似于树的层次遍历,但由于图没有树结构的父母孩子结点的单向约束关系,因此需要设置一个数组来标识结点以免被重复访问。
3.1.1基本原理:
①构建一个辅助数组保证每个结点只被访问一遍;构建一个辅助队列进行广度遍历;
②首先访问该节点,并将在辅助数组更改标识‘已访问’,并入队该节点;
③若队列不空,则进行循环:执行出队操作;检查队头结点的所有邻接点:
若邻接点被访问过,则跳过;若未访问过,执行步骤②。
bool visited[maxvem];
void BFStransver(Graph G){
for (i=0;u<G.vexnum;i++) visited[i]=False;
InitQueue(Q);
for(i=0;i<G.vexnum;i++) if(!visited[i]) BFS(G,i);
}
void BFS(Graph g,int v){
visit(v);
visited[v] = True;
Enqueue(Q,v);
while(!Isempty(Q)){
Dequeue(Q,v);
for(w=firstneighbor(G,v);w>=0;w=newneighbor(G,v,w)){
if(!visited[w]){
visit(w);
visited[w]=True;
Enqueue(Q,v);
}
}
}
}
3.1.2性能分析
1、每个结点都要进入队列进行遍历,空间复杂度O(V);
2、时间复杂度:
①邻接表遍历:
遍历节点表以及每个结点后的边表,时间复杂度O(V+E)
②邻接矩阵遍历:
遍历邻接表全部元素,时间复杂度O(V^2)
3.2深度优先遍历(DFS)
3.2.1基本原理
与BFS不同,DFS是递归的,不需要构建辅助队列;
①构建辅助数组;
②访问节点;访问该节点邻居;
③若邻居存在,则重复②;
bool visited[maxvex];
void DFStranverse(Graph G){
for(i=0;i<G.vexnum;i++) visited[i]=False;
for(i=0;i<G.vexnum;i++){
if(!visited[i]) DFS(G,i));
}
}
void DFS(Graph G, int v){
visit(v);
visited[v] = True;
for(w=firstneighbor(G,w);w>=0;w=nextneighbor(G,v.w)){
if(!visited[w]) DFS(G,w)
}
}
3.2.2性能分析
1、空间复杂度:DFS是递归算法,因此空间复杂度是O(V);
2、时间复杂度:
①邻接矩阵:O(V^2);
②邻接表:O(V+E);
3.3遍历-真题
- 01 设计一个算法,判断一个无向图G是否是一棵树。若是一棵树,则返回true;否则返回false;
思路:树是图的一种特殊形态,“边数+1=节点数”是树区别于图的特殊性质;
基本原理:利用DFS遍历图,得到图的结点和边个数,并与图中边的个数相比较;
int visited[maxvex]={0};//设置辅助数组
//调用递归,根据上述特性判别是否为树结构
void Istree(Graph &G){
int Vnum=0,Enum=0;
DFS(G,1,Vnum,Enum,visited);
if(Vnum==G.vexnum && Vnum==2*(G,vexnum-1))
return true;
else
return false;
}
//深度遍历
void DFS(Graph& G,int v,int& Vnum,int& Enum,int vistied[]){
visited[v]=1;//访问结点并置结点为已访问状态
Vnum++;
for(w=neighbor(G,v);w>=0;w=newneighbor(G,v,w))
//遍历结点的邻接点,若某一邻接点未访问,则递归
Enum++;
if(!visited[w])
DFS(G,w,Vnun,Enum,visited);
}
- 02写出图的深度优先搜索DFS算法的非递归算法(图用邻接表形式给出)
原理:非递归则引入栈;首先入栈结点、更改结点访问状态;当栈非空时,出栈,将出栈结点的所有邻接点访问后入栈;重复上述操作;
//设辅助数组;
int visited[maxvex] = {0};
void DFS_ndg(Graph& G,int v){
InitStack(S);
//访问结点并更改访问状态
visited[v] = 1;
Push(S,v);
while(!Isempty(S)){
Pop(S,v);
for(w=neighbor(G,v);w=0;w=neighbor(G,v,w))
if(!visited[w]){
visited[v]=1;
Push(S,v);
}
- 03分别采用基于深度优先遍历和广度优先遍历算法判别以邻接表方式存储的有向图中是否存在有顶点i到顶点j的路径(i!=j)主义算法中设计的图的操作必须在此存储结构上实现;
//辅助数组
int visited[maxvex]={0};
//深度优先遍历有向图找i到j的路径
bool Isroute_dfs(Graph& G,int i,int j){
int mark = DFS(G,i,j);
if(!mark) return 1;
eles return 0;
}
int DFS(Graph& G, int v, int j){
if(v!=j){
visit(v);
visited[v]=1;
for(w=neighbor(G,v);w>=0;w=neighbor(G,v,w)){
if(visited[w])
DFS(G,w,j)
}else
return 1;