【数据结构】无向图基本操作:手动输入邻接矩阵;文件读取邻接矩阵;手动输入邻接表

基础概念

图的相关概念和术语如下:

图:由顶点的有穷非空集合与边的集合组成。G=\left ( V,E \right )表示由顶点集合V和边集合E组成的无向图G

  1. 顶点(Vertex):图中的一个节点。
  2. 边(Edge):连接两个顶点的线段。
  3. 无向图(Undirected Graph):边没有方向的图。
  4. 有向图(Directed Graph):边有方向的图。
  5. 加权图(Weighted Graph):边带有权值的图。
  6. 多重图(Multigraph):两个顶点之间可以有多条边的图。
  7. 度(Degree):与一个顶点相连的边的条数。
  8. 路径(Path):从一个顶点到另一个顶点经过的边的序列。
  9. 简单路径(Simple Path):路径中不包含重复的顶点。
  10. 回路(Cycle):起点和终点相同的路径。
  11. 连通图(Connected Graph):图中任意两个顶点都有路径相连。
  12. 强连通图(Strongly Connected Graph):有向图中任意两个顶点都有路径相连。
  13. 子图(Subgraph):由原图中一部分顶点和边组成的图

邻接矩阵

基本思想:用一个一维数组存储图中顶点的信息,用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。

假设图G=(VE)有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问答icon-default.png?t=N7T8https://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;
}

运行结果

邻接表

  邻接表是图的链式存储结构。在无向图的邻接表中,顶点通常按编号顺序将顶点数据存储在一维数组中;用线性链表存储关联同一顶点的边。 

  1. 无向图的邻接表的特点
  2. 在G邻接表中,同一条边对应两个结点;
  3. 顶点v的度:等于v对应线性链表的长度;
  4.  判定两顶点v ,u是否邻接:要看v对应线性链表中有无对应的结点
  5. 在G中增减边:要在两个单链表插入、删除结点;
  6. 设存储顶点的一维数组大小为m(m³图的顶点数n), 图的边数为e,G占用存储空间为:m+2*e。G占用存储空间与 G 的顶点数、边数均有关;适用于边稀疏的图

示意图如下

上图中,最左侧的是头结点,此后为邻接表结点,第i个单链表中的结点表示依附于顶点v_{i}的边。在后续的学习中,我们会用到不加头结点的邻接表。

对于一个n个结点,e条边的图来说,其邻接表需要的存储空间为n+2e,故邻接表适合于稀疏图。在建立邻接表时,若输入的顶点信息即为顶点的编号,则建立邻接表的时间复杂度为O(n+e),否则必需遍历查找才能得到顶点在图中的位置,时间复杂度为O(n\cdot e)

代码

 先定义表结点和头结点

#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;
}

想建立的图:

运行结果

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值