邻接矩阵建立有/无向图的思想解析与代码实现

图的定义

既然了解到图,想必都了解线性表和树形结构,他们都属于简单结构
线性表:一对一关系,每个元素对应前驱和后继。
树形结构:一对多关系,一个根结点对应多个孩子。

名称解释:
图:多对多关系,是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。图不能为空,而线性表和树可以为空表或空树。

边:无方向
弧:有方向
无向边:顶点与顶点之间的边没有方向,用(Vi,Vj)表示。
有向弧:顶点与顶点之间的边有方向,用<Vi,Vj>表示,Vi为弧尾,Vj为弧头
简单图:不存在顶点到其自身的边,其同一条边不重复出现
无向完全图:在无向图中,任意两个顶点间都存在边,含n个顶点的无向完全图有 n * (n-1) / 2 条边。
有向完全图:在有向图中,任意两个顶点间都存在方向互为相反的两条弧,含n个顶点的无向完全图有 n * (n-1) 条边。
稀疏图:边或弧数小于 n * logn,反之为稠密图
网:带权的图
连通图:无向图中任意两个顶点都是连通的。
连通分量:无向图中极大连通子图。
强连通图:在有向图中,对于每一个Vi到Vj都存在路径。
强连通分量:有向图中的极大强连通子图。
连通图生成树:一个极小的连通子图,含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边。

邻接矩阵:
顶点用一维来记录;每个顶点的边用二维数组来记录。

无向图特点:
某个顶点的度等于在个顶点Vi在邻接矩阵中第 i 行(或第 j 列)的元素之和
求顶点Vi的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[ i ][ j ]为1就是邻接点
矩阵关于对角线对称。

有向图特点:
某一顶点的出度等于该顶点对应的行元素之和。
某一顶点的入度等于该顶点对应的列元素之和。

无向图的描述: 一个指针数组指向所有连接的结点,一个数据,一个连接数
有向图的描述: 一个指针数组指向所被连接的结点,一个数据,一个出度,一个入度

有向图/无向图建立代码:

本人原本分开写,但是觉得分开的话很难将有向无向的特点体现出来,因此结合在一起实现,大家如何想理解代码,建议先编译执行一下,了解实现的功能,在入手代码会比较清晰。

#include <stdio.h>
#include <stdlib.h>

#define MAX_VEXNUM 100

typedef struct adjoin_Matrix_List{
	char *vex_buf;//顶点数组
	int **adjmat;//二维邻接矩阵
	int vex_num;//顶点个数
	int edge_num;//边数(本人不知道有何作用- -,看别的文章都有定义)
}adjMatlist, *p_adjMatlist;

typedef struct NODIR_GRAPH_VEX{
	struct NODIR_GRAPH_VEX **next;//用作指针数组,指向所有连接的顶点
	char vertex;//顶点数值
	int num;//当前结点的连接数
}nodir_graph_vex, *p_nodir_graph_vex;

typedef struct DIR_GRAPH_VEX{
	struct DIR_GRAPH_VEX **next;//用作指针数组,指向所有被连接顶点
	char vertex;//顶点数值
	int in_degree, out_degree; //出入度
}dir_graph_vex, *p_dir_graph_vex;

int init_adjMatrix(p_adjMatlist *list)
{
	int i,j,flag;
	*list = (p_adjMatlist )malloc(sizeof(adjMatlist));
	printf("请输入顶点数:");
again1:
	scanf("%d", &(*list)->vex_num);
	if((*list)->vex_num > MAX_VEXNUM){
		printf("不能大于100!请重新输入\n");
		goto again1;
	}
	printf("请输入边数: ");
	scanf("%d", &(*list)->edge_num);
	(*list)->vex_buf = (char *)malloc(sizeof(char) * (*list)->vex_num);
	(*list)->adjmat = (int **)malloc(sizeof(int *) * (*list)->vex_num);
	for( i = 0; i<(*list)->vex_num; i++ ){
		(*list)->adjmat[i] = (int *)malloc(sizeof(int) * (*list)->vex_num);
		printf("请输入第%d个顶点的数值:",i+1);
		scanf(" %c",&(*list)->vex_buf[i]);
	}
	printf("/t**************************************\n\t\t1:无向图\t2:有向图\n\t**************************************\n请输入数字:");
again2:
	scanf(" %d", &flag);
	if(flag != 1 && flag != 2){
		printf("请输入1或2\n");
		goto again2;
	}
	//无向图的邻接矩阵
	if(flag == 1){
		//对角线初始化
		for( i = 0; i<(*list)->vex_num; i++ )
			(*list)->adjmat[i][i] = 0;//正无穷∞
		//边信息初始化
		printf("从矩阵对角线右边第一个开始\n");
		for( i = 0; i<(*list)->vex_num-1; i++ ){
			printf("请输入第%d个顶点的边信息:",i+1);
			for(j=i+1;j<(*list)->vex_num; j++){
				scanf( "%d", &( (*list)->adjmat[i][j] ) );
				(*list)->adjmat[j][i] = (*list)->adjmat[i][j];
			}
			printf("\n");
		}
		printf("\t\t已输入完毕!\n");
	}
	//有向图的邻接矩阵
	else
	{
		//对角线初始化
		for( i = 0; i<(*list)->vex_num; i++ )
			(*list)->adjmat[i][i] = 9999;//正无穷∞
		//边信息初始化
		printf("不输入对角线信息!\n");
		for( i = 0; i<(*list)->vex_num; i++ ){
			printf("请输入第%d个顶点的边信息:",i+1);
			for(j=0;j<(*list)->vex_num; j++){
				if(i==j)
					continue;
				scanf( "%d", &( (*list)->adjmat[i][j] ) );
			}
			printf("\n");
		}
		printf("\t\t已输入完毕!\n");
	}
	//打印邻接矩阵
	printf("**************************邻接矩阵表***********************\n\t\t");
	for( i = 0; i<(*list)->vex_num+1; i++ )
		printf("%c\t",(*list)->vex_buf[i]);
	for( i = 0; i<(*list)->vex_num; i++ )
	{
		printf("\n\t%c", (*list)->vex_buf[i]);
		for(j=0; j<(*list)->vex_num; j++)
			printf("%8d", (*list)->adjmat[i][j]);
	}
	printf("\n");
	return flag;
}

void creat_nodir_graph(p_adjMatlist list, p_nodir_graph_vex *graph)
{
	int i,j;
	p_nodir_graph_vex tmp;//打印用
	*graph = (p_nodir_graph_vex)malloc(sizeof(nodir_graph_vex) * list->vex_num);//定义若干顶点
	for( i=0; i<list->vex_num; i++ ){
		(*graph)[i].next = (p_nodir_graph_vex *)malloc(sizeof(p_nodir_graph_vex));
		(*graph)[i].vertex = list->vex_buf[i];
		(*graph)[i].num = 0;
	}
	//遍历矩阵右上角
	for(i=0; i<list->vex_num; i++){
		for(j=i+1; j<list->vex_num; j++){
			//有连接
			if( list->adjmat[i][j] != 0 ){
				//连接顶点申请内存
				if( (*graph)[i].num == 0 )
					*((*graph)[i].next) = (p_nodir_graph_vex)malloc(sizeof(nodir_graph_vex));
				else
					*((*graph)[i].next) = (p_nodir_graph_vex)realloc(*((*graph)[i].next), sizeof(nodir_graph_vex) * ( (*graph)[i].num+1 ) );
				//被连接的顶点申请内存
				if( (*graph)[j].num == 0 )
					*((*graph)[j].next) = (p_nodir_graph_vex)malloc(sizeof(nodir_graph_vex));
				else
					*((*graph)[j].next) = (p_nodir_graph_vex)realloc(*((*graph)[j].next), sizeof(nodir_graph_vex) * ( (*graph)[j].num+1 ) );
				//相互指向
				(*((*graph)[i].next))[ (*graph)[i].num ] = (*graph)[j];
				(*((*graph)[j].next))[ (*graph)[j].num ] = (*graph)[i];
				//连接数自增
				(*graph)[i].num++;
				(*graph)[j].num++;
			}
		}
		//打印每个结点的连接情况
		tmp = *((*graph)[i].next);
		printf("顶点%c连接情况:", (*graph)[i].vertex);
		for( j=0; j<(*graph)[i].num; j++)
		{
				printf("%c\t", (*((*graph)[i].next))[j].vertex );
		}
		printf("\n");
	}
}

void creat_dir_graph(p_adjMatlist list, p_dir_graph_vex *graph)
{
	int i,j;
	p_dir_graph_vex tmp;//打印用
	*graph = (p_dir_graph_vex)malloc(sizeof(dir_graph_vex) * list->vex_num);//定义若干顶点
	for( i=0; i<list->vex_num; i++ ){
		(*graph)[i].next = (p_dir_graph_vex *)malloc(sizeof(p_dir_graph_vex));
		(*graph)[i].vertex = list->vex_buf[i];
		(*graph)[i].in_degree = (*graph)[i].out_degree = 0;
	}
	//遍历矩阵右上角
	for(i=0; i<list->vex_num; i++){
		for(j=0; j<list->vex_num; j++){
			if( i==j )
				continue;
			//有连接
			if( list->adjmat[i][j] != 0 ){
				//连接顶点申请内存
				if( (*graph)[i].out_degree == 0 )
					*((*graph)[i].next) = (p_dir_graph_vex)malloc(sizeof(nodir_graph_vex));
				else
					*((*graph)[i].next) = (p_dir_graph_vex)realloc(*((*graph)[i].next), sizeof(nodir_graph_vex) * ( (*graph)[i].out_degree+1 ) );

				//指向弧头
				(*((*graph)[i].next))[ (*graph)[i].out_degree ] = (*graph)[j];
				//连接数自增
				(*graph)[i].out_degree++;
				(*graph)[j].in_degree++;
			}
		}
		//打印每个结点的连接情况
		tmp = *((*graph)[i].next);
		printf("顶点%c出度%d入度%d,连接情况:%c->", (*graph)[i].vertex, (*graph)[i].out_degree, (*graph)[i].in_degree, (*graph)[i].vertex );
		for( j=0; j<(*graph)[i].out_degree; j++)
		{
				printf("%c->", (*((*graph)[i].next))[j].vertex );
		}
		printf("null\n");
	}
}

int main()
{
	int ret;
	p_adjMatlist list = NULL;
	
	ret = init_adjMatrix(&list);
	if(ret == 1){
		p_nodir_graph_vex graph;
		creat_nodir_graph(list, &graph);
	}
	else{
		p_dir_graph_vex graph;
		creat_dir_graph(list, &graph);
	}
	return 0;
}

结果总结分析

  1. 无向图关于对角线对称,所以只需要初始化右上角或左下角的边信息,连接顶点时,双方同时建立连接,连接的顶点用二级指针(指针数组保存),连接数用于判断当前节点已连接的数量,重新申请内存存放于一级指针数组最后一个中,尾插法。
  2. 有向图需要初始化除对角线外的所有边信息,出度由该顶点对应的行元素有连接之和,入度对应该顶点对应的列元素有连接之和,连接时弧尾顶点出度自增,弧头顶点入度自增。
  3. 有向图中,遍历到某个顶点时,对于入度将无法确定,因为要等到所有顶点有遍历完毕才能确定入度情况(所有顶点都有可能指向当前顶点)。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值