基础概念
图的相关概念和术语如下:
图:由顶点的有穷非空集合与边的集合组成。表示由顶点集合和边集合组成的无向图。
- 顶点(Vertex):图中的一个节点。
- 边(Edge):连接两个顶点的线段。
- 无向图(Undirected Graph):边没有方向的图。
- 有向图(Directed Graph):边有方向的图。
- 加权图(Weighted Graph):边带有权值的图。
- 多重图(Multigraph):两个顶点之间可以有多条边的图。
- 度(Degree):与一个顶点相连的边的条数。
- 路径(Path):从一个顶点到另一个顶点经过的边的序列。
- 简单路径(Simple Path):路径中不包含重复的顶点。
- 回路(Cycle):起点和终点相同的路径。
- 连通图(Connected Graph):图中任意两个顶点都有路径相连。
- 强连通图(Strongly Connected Graph):有向图中任意两个顶点都有路径相连。
- 子图(Subgraph):由原图中一部分顶点和边组成的图
邻接矩阵
基本思想:用一个一维数组存储图中顶点的信息,用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。
假设图G=(V,E)有n个顶点,则邻接矩阵是一个n×n的方阵,定义为:
邻接矩阵主对角线为 0 且一定是对称矩阵。
手动输入
代码
下标从0开始:
想建立的图
#include <stdio.h>
#include <stdlib.h>
#define MAX 20
typedef struct {
int AdjMatrix[MAX];//顶点向量
int Edges[MAX][MAX];//邻接矩阵
}MGraph;
void CreatGraph(MGraph &G,int &n,int &m)
{
//确定顶点向量(顶点个数)
int i,j;
printf("输入顶点的个数\n");
scanf("%d",&n);
for(i=0;i<n;i++)
G.AdjMatrix[i]=i;
//边矩阵初始化
for(i=0;i<n;i++)
for(j=0;j<n;j++)
G.Edges[i][j]=0;
//输入边矩阵
int p,q;
printf("输入边的条数\n");
scanf("%d",&m);
for(i=1;i<=m;i++)
{
printf("输入要建立边的顶点(0-%d)\n",n-1);
scanf("%d,%d",&p,&q);
G.Edges[p][q]=1;
G.Edges[q][p]=1;
}
//输出边矩阵
printf("创建无向图边关系如下:\n");
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
printf("%3d",G.Edges[i][j]);
}
printf("\n");
}
}
int main(void)
{
MGraph G;
G=*(MGraph*)malloc(sizeof(MGraph));
int m,n;
CreatGraph(G,n,m);
return 0;
}
下标从1开始:
想要建立的图:
#include <stdio.h>
#include <stdlib.h>
#define MAX 20
typedef struct {
int AdjMatrix[MAX];//顶点向量
int Edges[MAX][MAX];//邻接矩阵
}MGraph;
void CreatGraph(MGraph &G,int &n,int &m)
{
//确定顶点向量(顶点个数)
int i,j;
printf("输入顶点的个数\n");
scanf("%d",&n);
for(i=1;i<=n;i++)
G.AdjMatrix[i]=i;
//边矩阵初始化
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
G.Edges[i][j]=0;
//输入边矩阵
int p,q;
printf("输入边的条数\n");
scanf("%d",&m);
for(i=1;i<=m;i++)
{
printf("输入要建立边的顶点(0-%d)\n",n);
scanf("%d,%d",&p,&q);
G.Edges[p][q]=1;
G.Edges[q][p]=1;
}
//输出边矩阵
printf("创建无向图边关系如下:\n");
for(i=1;i<=n;i++)
{
printf("V%d ",i);
for(j=1;j<=n;j++)
{
printf("%3d",G.Edges[i][j]);
}
printf("\n");
}
}
int main(void)
{
MGraph G;
G=*(MGraph*)malloc(sizeof(MGraph));
int m,n;
CreatGraph(G,n,m);
return 0;
}
运行结果
下标从0开始:
下标从1开始:
文件读取
建立文本文档的位置:和cpp(或c)、Debug在同一个文件夹下。简单来说,找到你可以直接打开执行编译器代码的文件所在的文件夹,把文本文档也建立在这个文件夹下。
把图的邻接矩阵以文本文件的形式存储在磁盘上,文件的第一行存储图的顶点的个数,后面以方阵形式存储图的邻接矩阵,0代表无邻接边。
如果代码正确但打不开文件,将建立的文件的拓展名".txt"删除,但不改动代码
正文中的代码和运行结果都是正确的,可以放心食用。博主文件知识很薄弱,以下蓝字部分为调试过程的经验,给同样对文件不熟的读者参考。如果你文件掌握正常,可以跳过这一部分。
如果打印失败(即只显示Press any key to continue,程序运行空白),可尝试将函数类型改为void,将文件打开失败的代码删除:
把这些↓
if((fp=fopen("matrix.txt","r"))==NULL)
{
printf("文件打开失败\n");
return 0;
}
用这一行替代↓
fopen("matrix.txt","r");
如果运行成功,此时再改回原代码,再次运行。
调试过程中若有疑问,可以参考一下高手们的回答:
C语言从文件读取无向图邻接矩阵_编程语言-CSDN问答https://ask.csdn.net/questions/8027387
建立的文件:
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
typedef struct {
int Edges[MAX][MAX];//邻接矩阵
int vexnum;//当前顶点个数
}MGraph;
int CreatGraph(MGraph &G)
{
FILE *fp;
if((fp=fopen("matrix.txt","r"))==NULL)
{
printf("文件打开失败\n");
return 0;
}
fscanf(fp,"%d",&G.vexnum);
int i,j;
for(i=0;i<G.vexnum;i++)
{
for(j=0;j<G.vexnum;j++)
fscanf(fp,"%d",&G.Edges[i][j]);
}
fclose(fp);
//输出边矩阵
printf("创建无向图边关系如下:\n");
for(i=0;i<G.vexnum;i++)
{
for(j=0;j<G.vexnum;j++)
{
printf("%3d",G.Edges[i][j]);
}
printf("\n");
}
return 1;
}
int main(void)
{
MGraph G;
G=*(MGraph*)malloc(sizeof(MGraph));
CreatGraph(G);
return 0;
}
运行结果
邻接表
邻接表是图的链式存储结构。在无向图的邻接表中,顶点通常按编号顺序将顶点数据存储在一维数组中;用线性链表存储关联同一顶点的边。
- 无向图的邻接表的特点
- 在G邻接表中,同一条边对应两个结点;
- 顶点v的度:等于v对应线性链表的长度;
- 判定两顶点v ,u是否邻接:要看v对应线性链表中有无对应的结点
- 在G中增减边:要在两个单链表插入、删除结点;
- 设存储顶点的一维数组大小为m(m³图的顶点数n), 图的边数为e,G占用存储空间为:m+2*e。G占用存储空间与 G 的顶点数、边数均有关;适用于边稀疏的图
示意图如下
上图中,最左侧的是头结点,此后为邻接表结点,第个单链表中的结点表示依附于顶点的边。在后续的学习中,我们会用到不加头结点的邻接表。
对于一个个结点,条边的图来说,其邻接表需要的存储空间为,故邻接表适合于稀疏图。在建立邻接表时,若输入的顶点信息即为顶点的编号,则建立邻接表的时间复杂度为,否则必需遍历查找才能得到顶点在图中的位置,时间复杂度为。
代码
先定义表结点和头结点
#define MAX 20
typedef struct node{
int vex;//顶点编号
struct node *next;//指向下一个结点
}edgenode;//
typedef struct vnode{
int vertex;//顶点V_{i}
edgenode *firstnode;//指向第一个表结点
}vertexnode;//头结点
完整代码:
#include <stdio.h>
#include <stdlib.h>
#define MAX 20
typedef struct node{//邻接表结点
int adjvex;//邻接点域
struct node *next;//指向下一个邻接边结点的指针域
}edgenode;//邻接表结点类型
typedef struct vnode{//顶点表结点
int vertex;//顶点域
edgenode *firstedge;//指向邻接表第一个邻接边结点的指针域
}vertexnode;//顶点表结点类型
void Creatlist(vertexnode g[],int n,int e)//n为顶点数,e为边数
{
edgenode *p;int i;//数组从1开始使用,0闲置
for(i=1;i<=n;i++)//头结点结构体数组初始化
{
g[i].vertex=i;
g[i].firstedge=NULL;
}
printf("已建立顶点如下\n");
for(i=1;i<=n;i++)
printf("V%d",i);
int j,k;
for(k=1;k<=e;k++)
{
printf("输入要建立的边:i,j\n");
scanf("%d,%d",&i,&j);
//在结点i的单链表插入结点j
p=(edgenode*)malloc(sizeof(edgenode));
p->adjvex=j;
p->next=g[i].firstedge;
g[i].firstedge=p;
//在结点j的单链表插入结点i
p=(edgenode*)malloc(sizeof(edgenode));
p->adjvex=i;
p->next=g[j].firstedge;
g[j].firstedge=p;
}
}
void PrintfList(vertexnode g[],int n)//顶点数n,边数e
{
edgenode *p;
int i;
for(i=1;i<=n;i++)
{
printf("V%d->",i);
p=g[i].firstedge;//指向第一个表结点
while(p!=NULL)
{
printf("%d->",p->adjvex);
p=p->next;
}
printf("NULL\n");
}
}
int main(void)
{
vertexnode g[MAX];
int n,e;
printf("输入顶点数\n");
scanf("%d",&n);
printf("输入边数\n");
scanf("%d",&e);
Creatlist(g,n,e);
PrintfList(g,n);
return 0;
}
想建立的图: