目录
因图中可能存在回路,某些顶点可能会被重复访问,那么如何避免遍历不会因回路而陷入死循环。
图的存储结构
完整代码:
#include"stdio.h"
#include"stdlib.h"
typedef int datatype;
#define N 10
int vertexnum=4,vertexarc=4;
int i,j;
//用于存储图的邻接矩阵的数组
struct node
{
datatype vertex[N];
int arc[N][N];
int degree_out[N]; //顶点的出度
int degree_in[N]; //顶点的入度
}mGraph;
//用于存储图的邻接表的顶点结点和边表结点
typedef struct ArcNode
{ int adjvex;
struct ArcNode *next;
}Arcnode;
struct VertexNode
{
datatype vertex;
Arcnode *firstedge;
int degree_out; //顶点的出度
int degree_in; //顶点的入度
} adjlist[N];
int visited[N];
//初始化visited数组
void initVisited(int vertexNum)
{
int i;
for (i=0; i<vertexNum; i++)
visited[i] = 0;
}
int creatGraph(int vertexnum,int vertexarc)//创建邻接矩阵
{
int l,k,j,i;
printf("请输入四个顶点的值\n");
for(int i=0;i<vertexnum;i++)
scanf("%d",&mGraph.vertex[i]);
for(int i=0;i<vertexnum;i++)
for(int j=0;j<vertexnum;j++)
mGraph.arc[i][j]=0;
printf("请输入每条边的两个顶点\n");
for(i=0;i<vertexarc;i++)
{
scanf("%d%d",&l,&k);
l--;k--;
mGraph.arc[l][k]=1;
mGraph.degree_out[l]++;
mGraph.degree_in[k]++;
}
}
int ALgraph(int vertexnum,int vertexarc)//邻接表的创建
{
Arcnode *p;
char a[vertexnum];
puts("请输入四个顶点的值");
for(i=0;i<vertexnum;i++)
{
scanf("%d",&adjlist[i].vertex);
adjlist[i].firstedge=NULL;
}
puts("输入每条边的两个顶点");
int k,l;//用于接收边的两个顶点
for(int j=0;j<vertexarc;j++)
{
scanf("%d%d",&k,&l);
k--;l--;
adjlist[k].degree_out++;
adjlist[l].degree_in++;
p=(Arcnode *)malloc(sizeof(Arcnode));
p->adjvex=l;
p->next=adjlist[k].firstedge;
adjlist[k].firstedge=p;
}
}
void display(int vertexnum,int vertexarc)//输出图的邻接矩阵
{
int i,j;
puts("输出顶点的值");
for(i=0;i<vertexnum;i++)
printf("%d ",mGraph.vertex[i]);
putchar('\n');
puts("输出邻接矩阵");
for(i=0;i<vertexnum;i++)
{for(j=0;j<vertexnum;j++)
printf("%d ",mGraph.arc[i][j]);
putchar('\n');
}
}
void displayAlgraph(int vertexnum)//邻接表的输出
{
/*
puts("输出顶点的值为");
for(i=0;i<vertexnum;i++)
{
printf("%d ",adjlist[i].vertex);
}*/
/*
puts("输出顶点以及邻接边");
for(i=0;i<vertexnum;i++)
{
printf("%d ",adjlist[i].vertex);
printf(":");
printf("(%d,%d) ",adjlist[i].vertex,)
} */
ArcNode *p;
for(i=0;i<vertexnum;i++)
{
p=adjlist[i].firstedge;
printf("%d :",i+1);
while(p!=NULL)
{
//printf("%d ",p->adjvex);
printf("(%d,%d) ",adjlist[i].vertex,adjlist[p->adjvex].vertex);
p=p->next;
}
printf("\n");
}
}
void tongji(int vertexnum)//邻接矩阵统计出度和入度
{
for(int i=0;i<vertexnum;i++)
printf("顶点 %d 的出度为:%d 入度为%d\n",mGraph.vertex[i],mGraph.degree_out[i],mGraph.degree_in[i]);
}
void tongji2(int vertexnum)//邻接表统计出度和入度
{
for(i=0;i<vertexnum;i++)
{
printf("顶点%d 的出度为:%d 入度为: %d\n",adjlist[i].vertex,adjlist[i].degree_out,adjlist[i].degree_in);
}
}
int DFSTraversel(int v)//基于邻接矩阵的深度优先遍历
{
printf("%d ",mGraph.vertex[v]);
visited [v]=1;
for (int j=0; j<vertexnum; j++)
if (mGraph.arc[v][j]==1 && visited[j]==0)
DFSTraversel( j );
}
int DFSTraverse(int v)//基于邻接表的深度优先遍历
{
Arcnode *p;
p=adjlist[v].firstedge;
printf("%d ",adjlist[v].vertex);
visited[v]=1;
while(p)
{
j=p->adjvex;
if(visited[j]==0)
DFSTraverse(j);
p=p->next;
}
}
/*
void enquene(linklist *rear,int v)
{
linklist *T;
T=(linklist *)malloc(sizeof(linklist));
T->data=v;
T->next=NULL;
rear->next=T;
rear=T;
}*/
int DFSTraversel2(int v)//基于邻接矩阵的广度优先遍历
{
printf("基于邻接矩阵的广度优先遍历");
/*
Arcnode *p;
linklist *L;
L=(linklist *)malloc(sizeof(linklist));
L->front=L->rear=0;
linkqene *rear,*front;
front=(linkqene *)malloc(sizeof(linkqene));
front->next=NULL;
rear=front;
printf("%d",mGraph.vertex[v]);
visited[v]=1;
enquene(rear,v);//入队 */
int rear=-1,front=-1;
int Q[vertexnum];
printf("%d ",mGraph.vertex[v]);
visited[v]=1;
Q[++rear]=v;
while(front!=rear)
{
v=Q[++front];
for(i=0;i<vertexnum;i++)
{
if(mGraph.arc[v][i]==1&&visited[i]==0)
{
printf("%d ",mGraph.vertex[i]);
visited[i]=1;
Q[++rear]=i;
}
}
}
}
int DFSTraverse2(int v)//基于邻接表的广度优先输出
{
Arcnode *p;
int rear=-1,front=-1;
int Q[vertexnum];
printf("%d ",adjlist[v].vertex);
visited[v]=1;
Q[++rear]=v;
while(rear!=front)
{
v=Q[++front];
p=adjlist[v].firstedge;
while(p)
{
j=p->adjvex;
if(visited[j]==0)
{
printf("%d ",adjlist[j].vertex);
visited[j]=1;
Q[++rear]=j;
}
p=p->next;
}
}
}
void menu()
{
puts("\t\t0:退出程序");
puts("\t\t1:创建图的邻接矩阵存储结构");
puts("\t\t2:创建图的领接表存储方式");
puts("\t\t3:输出图的邻接矩阵");
puts("\t\t4:输出图的邻接表");
puts("\t\t5:在邻接矩阵上深度优先遍历图结构");
puts("\t\t6:在邻接矩阵上广度优先遍历图结构");
puts("\t\t7:在邻接表上深度优先遍历图结构");
puts("\t\t8:在邻接表上广度优先遍历图结构");
puts("\t\t9:计算并输出图邻接矩阵中结点的度");
puts("\t\t10:计算并输出图邻接表中结点的度");
}
main()
{
/* int n,e;
printf("请输入顶点和边数个数\n");
scanf("%d%d",&n,&e);
int a[n]="0";
printf("请输入顶点的元素");
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
*/
// int vertexnum,vertexarc;
// printf("请输入你的顶点数和边数\n");
// scanf("%d%d",&vertexnum,&vertexarc);
menu();
printf("请输入你要选择的功能: ");
int n;
scanf("%d",&n);
while(n)
{
switch(n)
{
case 0:break;
case 1:creatGraph(vertexnum,vertexarc); //创建图的邻接矩阵结构
break;
case 2:ALgraph(vertexnum,vertexarc);// 创建图的邻接表结构
break;
case 3:display(vertexnum,vertexarc);//输出图的邻接矩阵
break;
case 4:displayAlgraph(vertexnum);//输出图的邻接表
break;
case 5:puts("深度优先遍历输出");
DFSTraversel(0);//基于邻接矩阵的深度优先遍历
putchar('\n');//换行
initVisited(vertexnum);//初始化visited数组
break;
case 6: DFSTraversel2(0);//基于邻接矩阵的广度优先遍历
putchar('\n');//换行
initVisited(vertexnum);//初始化visited数组
break;
case 7:
puts("深度优先遍历输出");
DFSTraverse(0);//基于邻接表的深度优先遍历
putchar('\n');//换行
initVisited(vertexnum);//初始化visited数组
break;
case 8:puts("广度优先遍历输出");
DFSTraverse2(0);//基于邻接表的广度优先遍历
putchar('\n');//换行
initVisited(vertexnum);//初始化visited数组
break;
case 9:tongji(vertexnum);//统计邻接矩阵的出度和入度
break;
case 10:tongji2(vertexnum);//统计邻接表的出度和入度
break;
}
menu();
scanf("%d",&n);
}
}
-
因图中可能存在回路,某些顶点可能会被重复访问,那么如何避免遍历不会因回路而陷入死循环。
解决方案:附设访问标志数组visited[n] 。
代码如下:
void initVisited(int vertexNum)
{
int i;
for (i=0; i<vertexNum; i++)
visited[i] = 0;
}
邻接矩阵实现图的存储结构
-
是否可以采用顺序存储结构存储图?
图的特点:顶点之间的关系是m:n,即任何两个顶点之间都可能存在关系(边),无法通过存 储位置表示这种任意的逻辑关系,所以,图无法采用顺序存储结构。
-
如何存储图?
考虑图的定义,图是由顶点和边组成的,分别考虑如何存储顶点、如何存储边。
-
邻接矩阵数组表示
基本思想:用一个一维数组存储图中顶点的信息,用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。
假设图G=(V,E)有n个顶点,则邻接矩阵是一个n×n的方阵,定义为:
arc[i][j]= 1 若(vi, vj)∈E(或<vi, vj>∈E)
0 其它
全局定义结构体
struct node
{
datatype vertex[N];
int arc[N][N];
int degree_out[N]; //顶点的出度
int degree_in[N]; //顶点的入度
}mGraph;
邻接矩阵创建图
1.确定图的顶点个数和边的个数;
2. 输入顶点信息;
3. 初始化邻接矩阵;
4. 依次输入每条边存储在邻接矩阵arc中;
4.1 输入边依附的两个顶点的序号i, j;
4.2 将邻接矩阵的第i行第j列的元素值置为1;
4.3 将邻接矩阵的第j行第i列的元素值置为1;
代码如下:
int creatGraph(int vertexnum,int vertexarc)//创建邻接矩阵
{
int l,k,j,i;
printf("请输入四个顶点的值\n");
for(int i=0;i<vertexnum;i++)
scanf("%d",&mGraph.vertex[i]);
for(int i=0;i<vertexnum;i++)
for(int j=0;j<vertexnum;j++)
mGraph.arc[i][j]=0;
printf("请输入每条边的两个顶点\n");
for(i=0;i<vertexarc;i++)
{
scanf("%d%d",&l,&k);
l--;k--;
mGraph.arc[l][k]=1;
}
}
邻接矩阵输出图
代码如下:
void display(int vertexnum,int vertexarc)//输出图的邻接矩阵
{
int i,j;
puts("输出顶点的值");
for(i=0;i<vertexnum;i++)
printf("%d ",mGraph.vertex[i]);
putchar('\n');
puts("输出邻接矩阵");
for(i=0;i<vertexnum;i++)
{for(j=0;j<vertexnum;j++)
printf("%d ",mGraph.arc[i][j]);
putchar('\n');
}
}
基于邻接矩阵深度优先遍历
⑴ 访问顶点v;
⑵ 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
⑶ 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。
代码如下:
int DFSTraversel(int v)//基于邻接矩阵的深度优先遍历
{
printf("%d ",mGraph.vertex[v]);
visited [v]=1;
for (int j=0; j<vertexnum; j++)
if (mGraph.arc[v][j]==1 && visited[j]==0)
DFSTraversel( j );
}
基于邻接矩阵的广度优先遍历
⑴ 访问顶点v;
⑵ 依次访问v的各个未被访问的邻接点v1, v2, …, vk;
⑶ 分别从v1,v2,…,vk出发依次访问它们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问。直至图中所有与顶点v有路径相通的顶点都被访问到。
代码如下:
int DFSTraversel2(int v)//基于邻接矩阵的广度优先遍历
{
printf("基于邻接矩阵的广度优先遍历");
int rear=-1,front=-1;
int Q[vertexnum];
printf("%d ",mGraph.vertex[v]);
visited[v]=1;
Q[++rear]=v;
while(front!=rear)
{
v=Q[++front];
for(i=0;i<vertexnum;i++)
{
if(mGraph.arc[v][i]==1&&visited[i]==0)
{
printf("%d ",mGraph.vertex[i]);
visited[i]=1;
Q[++rear]=i;
}
}
}
}
-
如何求顶点 i 的所有邻接点?
将数组中第 i 行元素扫描一遍,若arc[i][j]为1,则顶点 j 为顶点 i 的邻接点。
-
有向图的邻接矩阵一定不对称吗?
不一定,例如有向完全图。
-
如何判断从顶点 i 到顶点 j 是否存在边?
测试邻接矩阵中相应位置的元素arc[i][j]是否为1。
邻接表实现图的存储结构
-
图的邻接矩阵存储结构的空间复杂度?
假设图G有n个顶点e条边,则存储该图需要O(n2) 。
-
如果为稀疏图则会出现什么现象?
邻接表存储的基本思想:对于图的每个顶点vi,将所有邻接于vi的顶点链成一个单链表,称为顶点vi的边表(对于有向图则称为出边表),所有边表的头指针和存储顶点信息的一维数组构成了顶点表。
-
边表中的结点表示什么?
每个结点对应图中的一条边,邻接表的空间复杂度为O(n+2e)。
-
如何求顶点 i 的度?
顶点i的边表中结点的个数。
-
如何判断顶点 i 和顶点 j之间是否存在边?
测试顶点 i 的边表中是否存在终点为 j 的结点。
-
如何求顶点 i 的出度?
顶点 i 的出边表中结点的个数。
-
如何求顶点 i 的入度?
各顶点的出边表中以顶点 i 为终点的结点个数。
-
如何求顶点 i 的所有邻接点?
遍历顶点 i 的边表,该边表中的所有终点都是顶点 i 的邻接点。
定义邻接表结点
typedef struct ArcNode
{ int adjvex;
struct ArcNode *next;
}Arcnode;
struct VertexNode
{
datatype vertex;
Arcnode *firstedge;
int degree_out; //顶点的出度
int degree_in; //顶点的入度
} adjlist[N];
创建邻接表
1. 确定图的顶点个数和边的个数;
2. 输入顶点信息,初始化该顶点的边表;
3. 依次输入边的信息并存储在边表中;
3.1 输入边所依附的两个顶点的序号i和j;
3.2 生成邻接点序号为j的边表结点s;
3.3 将结点s插入到第i个边表的头部;
代码如下:
int ALgraph(int vertexnum,int vertexarc)//邻接表的创建
{
Arcnode *p;
puts("请输入四个顶点的值");
for(i=0;i<vertexnum;i++)
{
scanf("%d",&adjlist[i].vertex);
adjlist[i].firstedge=NULL;
}
puts("输入每条边的两个顶点");
int k,l;//用于接收边的两个顶点
for(int j=0;j<vertexarc;j++)
{
scanf("%d%d",&k,&l);
k--;l--;
adjlist[k].degree_out++;
adjlist[l].degree_in++;
p=(Arcnode *)malloc(sizeof(Arcnode));
p->adjvex=l;
p->next=adjlist[k].firstedge;
adjlist[k].firstedge=p;
}
}
输出邻接表
代码如下:
void displayAlgraph(int vertexnum)//邻接表的输出
{
ArcNode *p;
for(i=0;i<vertexnum;i++)
{
p=adjlist[i].firstedge;
printf("%d :",i+1);
while(p!=NULL)
{
//printf("%d ",p->adjvex);
printf("(%d,%d) ",adjlist[i].vertex,adjlist[p->adjvex].vertex);
p=p->next;
}
printf("\n");
}
}
基于邻接表的深度优先遍历
⑴ 访问顶点v;
⑵ 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
⑶ 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。
代码如下:
int DFSTraverse(int v)//基于邻接表的深度优先遍历
{
Arcnode *p;
p=adjlist[v].firstedge;
printf("%d ",adjlist[v].vertex);
visited[v]=1;
while(p)
{
j=p->adjvex;
if(visited[j]==0)
DFSTraverse(j);
p=p->next;
}
}
基于邻接表的广度优先遍历
⑴ 访问顶点v;
⑵ 依次访问v的各个未被访问的邻接点v1, v2, …, vk;
⑶ 分别从v1,v2,…,vk出发依次访问它们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问。直至图中所有与顶点v有路径相通的顶点都被访问到。
代码如下:
int DFSTraverse2(int v)//基于邻接表的广度优先输出
{
Arcnode *p;
int rear=-1,front=-1;
int Q[vertexnum];
printf("%d ",adjlist[v].vertex);
visited[v]=1;
Q[++rear]=v;
while(rear!=front)
{
v=Q[++front];
p=adjlist[v].firstedge;
while(p)
{
j=p->adjvex;
if(visited[j]==0)
{
printf("%d ",adjlist[j].vertex);
visited[j]=1;
Q[++rear]=j;
}
p=p->next;
}
}
}
主函数
main()
{menu();
printf("请输入你要选择的功能: ");
int n;
scanf("%d",&n);
while(n)
{
switch(n)
{
case 0:break;
case 1:creatGraph(vertexnum,vertexarc); //创建图的邻接矩阵结构
break;
case 2:ALgraph(vertexnum,vertexarc);// 创建图的邻接表结构
break;
case 3:display(vertexnum,vertexarc);//输出图的邻接矩阵
break;
case 4:displayAlgraph(vertexnum);//输出图的邻接表
break;
case 5:puts("深度优先遍历输出");
DFSTraversel(0);//基于邻接矩阵的深度优先遍历
putchar('\n');//换行
initVisited(vertexnum);//初始化visited数组
break;
case 6: DFSTraversel2(0);//基于邻接矩阵的广度优先遍历
putchar('\n');//换行
initVisited(vertexnum);//初始化visited数组
break;
case 7:
puts("深度优先遍历输出");
DFSTraverse(0);//基于邻接表的深度优先遍历
putchar('\n');//换行
initVisited(vertexnum);//初始化visited数组
break;
case 8:puts("广度优先遍历输出");
DFSTraverse2(0);//基于邻接表的广度优先遍历
putchar('\n');//换行
initVisited(vertexnum);//初始化visited数组
break;
}
menu();
scanf("%d",&n);
}
}