看数据结构写代码(35) 图的邻接矩阵表示法

杂谈:最近清明小长假,好好的放松了一下。节前 和 节后 都有点 松懈。不好,不好。贵在坚持。加油。

图的邻接矩阵表示法是用 两个数组 来表示 图的数据结构。一个是顶点数组,另一个是邻接矩阵数组。邻接矩阵 里存放着 顶点的关系。

用邻接矩阵表示图,在 看 顶点之间 是否有边,或者 求顶点的度等操作时比较简单。但空间浪费巨大,在插入,删除 顶点 和边 操作时 需要 移动大量数据,造成不便。所以在插入删除比较多,节点数比较多的时候 不宜 使用这种结构。

下面上代码:

源代码网盘地址:点击打开链接

// MGraph2.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <climits>
#include <cstring>

#define INFINITY INT_MAX
#define MAX_VERTEX_NUM 20
enum E_State
{
	E_State_Error = 0,
	E_State_Ok = 1,
};
enum E_Graph_Kind
{
	DG = 0,//有向图
	DN,//有向网
	UDG,//无向图
	UDN,//无向网
};
//边(弧)单元
typedef struct ArcCell
{
	int adj;//表示顶点的关系类型,对于无权图,0,1表示是否相邻,对于有权图,表示权值类型
	char * info;//边,弧其余信息.
}AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
//定义图
struct MGraph
{
	char  vexs[MAX_VERTEX_NUM];//顶点集
	AdjMatrix arcs;//图的邻接矩阵
	int vexNum,arcNum;
	E_Graph_Kind kind;
};

int graphLocation(MGraph graph,char  vex);
void createDG(MGraph * graph);
void createDN(MGraph * graph);
void createUDG(MGraph * graph);
void createUDN(MGraph * graph);

void graphCreate(MGraph * graph){
	E_Graph_Kind kind;
	printf("请输入要创建的图的类型(有向图:0,有向网:1,无向图:2,无向网:3)\n");
	scanf("%d",&kind);
	switch (kind){
	case DG:
		createDG(graph);
		break;
	case DN:
		createDN(graph);
		break;
	case UDG:
		createUDG(graph);
		break;
	case UDN:
		createUDN(graph);
		break;
	default:
		break;
	}
}
//返回顶点vex的第一个邻接点
char  firstAdjVex(MGraph graph,char vex){
	int location = graphLocation(graph,vex);
	int i = 0;
	for (; i < graph.vexNum; i++){
		if ((graph.kind == DG || graph.kind == UDG) && graph.arcs[location][i].adj == 1){
			return graph.vexs[i];
		}
		else if((graph.kind == DN || graph.kind == UDN) && graph.arcs[location][i].adj != INFINITY){
			return graph.vexs[i];
		}
	}
	return ' ';
}
//返回顶点vex1 相对于 vex2的下一个邻接点.
char  nextAdjVex(MGraph graph,char  vex1,char  vex2){
	int location1 = graphLocation(graph,vex1);
	int location2 = graphLocation(graph,vex2);
	int i = location2+1;
	for (; i < graph.vexNum; i++){
		if ((graph.kind == DG || graph.kind == UDG) && graph.arcs[location1][i].adj == 1){
			return graph.vexs[i];
		}
		else if((graph.kind == DN || graph.kind == UDN) && graph.arcs[location1][i].adj != INFINITY){
			return graph.vexs[i];
		}
	}
	return ' ';
}

//查找顶点的位置
int graphLocation(MGraph graph,char  vex){
	for (int i = 0; i < graph.vexNum; i++){
		if (graph.vexs[i] == vex){
			return i;
		}
	}
	return -1;
}
//创建图 子函数
//有向图
void createDG(MGraph * graph){
	graph->kind = DG;
	printf("请输入顶点数,边(弧)数\n");
	scanf("%d%d%*c",&graph->vexNum,&graph->arcNum);
	//初始化邻接矩阵
	for (int i = 0; i < MAX_VERTEX_NUM; i++){
		for (int j = 0; j < MAX_VERTEX_NUM; j++){
			graph->arcs[i][j].adj = 0;
			graph->arcs[i][j].info = NULL;
		}
	}
	//构造顶点集
	printf("请输入顶点集\n");
	for (int i = 0; i < graph->vexNum; i++){
		scanf("%c",&graph->vexs[i]);
	}
	//构造顶点关系
	fflush(stdin);
	printf("请输入顶点的关系\n");
	for (int i = 0; i < graph->arcNum; i++){
		char vex1,vex2;
		scanf("%c%c%*c",&vex1,&vex2);
		int location1 = graphLocation(*graph,vex1);
		int location2 = graphLocation(*graph,vex2);
		graph->arcs[location1][location2].adj = 1;
	}
}
//有向网
void createDN(MGraph * graph){
	graph->kind = DN;
	printf("请输入顶点数,边(弧)数\n");
	scanf("%d%d%*c",&graph->vexNum,&graph->arcNum);
	//初始化邻接矩阵
	for (int i = 0; i < MAX_VERTEX_NUM; i++){
		for (int j = 0; j < MAX_VERTEX_NUM; j++){
			graph->arcs[i][j].adj = INFINITY;
			graph->arcs[i][j].info = NULL;
		}
	}
	//构造顶点集
	printf("请输入顶点集\n");
	for (int i = 0; i < graph->vexNum; i++){
		scanf("%c",&graph->vexs[i]);
	}
	//构造顶点关系
	fflush(stdin);
	printf("请输入顶点的关系\n");
	for (int i = 0; i < graph->arcNum; i++){
		char vex1,vex2;
		int weight;
		scanf("%c%c%d%*c",&vex1,&vex2,&weight);
		int location1 = graphLocation(*graph,vex1);
		int location2 = graphLocation(*graph,vex2);
		graph->arcs[location1][location2].adj = weight;
	}
}
//无向图
void createUDG(MGraph * graph){
	graph->kind = UDG;
	printf("请输入顶点数,边(弧)数\n");
	scanf("%d%d%*c",&graph->vexNum,&graph->arcNum);
	//初始化邻接矩阵
	for (int i = 0; i < MAX_VERTEX_NUM; i++){
		for (int j = 0; j < MAX_VERTEX_NUM; j++){
			graph->arcs[i][j].adj = 0;
			graph->arcs[i][j].info = NULL;
		}
	}
	//构造顶点集
	printf("请输入顶点集\n");
	for (int i = 0; i < graph->vexNum; i++){
		scanf("%c",&graph->vexs[i]);
	}
	fflush(stdin);
	//构造顶点关系
	printf("请输入顶点的关系\n");
	for (int i = 0; i < graph->arcNum; i++){
		char vex1,vex2;
		scanf("%c%c%*c",&vex1,&vex2);
		int location1 = graphLocation(*graph,vex1);
		int location2 = graphLocation(*graph,vex2);
		graph->arcs[location1][location2].adj = graph->arcs[location2][location1].adj = 1;
	}
}
//无向网
void createUDN(MGraph * graph){
	graph->kind = UDN;
	printf("请输入顶点数,边(弧)数\n");
	scanf("%d%d%*c",&graph->vexNum,&graph->arcNum);
	//初始化邻接矩阵
	for (int i = 0; i < MAX_VERTEX_NUM; i++){
		for (int j = 0; j < MAX_VERTEX_NUM; j++){
			graph->arcs[i][j].adj = INFINITY;
			graph->arcs[i][j].info = NULL;
		}
	}
	//构造顶点集
	printf("请输入顶点集\n");
	for (int i = 0; i < graph->vexNum; i++){
		scanf("%c",&graph->vexs[i]);
	}
	//构造顶点关系
	fflush(stdin);
	printf("请输入顶点的关系\n");
	for (int i = 0; i < graph->arcNum; i++){
		char vex1,vex2;
		int weight;
		scanf("%c%c%d%*c",&vex1,&vex2,&weight);
		int location1 = graphLocation(*graph,vex1);
		int location2 = graphLocation(*graph,vex2);
		graph->arcs[location1][location2].adj =graph->arcs[location2][location1].adj = weight;
	}
}

//查看顶点数据之间是否相邻
bool graphIsAdj(MGraph graph,char vex1,char vex2){
	E_Graph_Kind kind = graph.kind;
	int weight = (kind == DG || kind == UDG) ? 0 : INFINITY;
	int location1 = graphLocation(graph,vex1);
	int location2 = graphLocation(graph,vex2);
	return graph.arcs[location1][location2].adj != weight ? true : false;
}

int graphDegree(MGraph graph,char vex){
	int location = graphLocation(graph,vex);
	E_Graph_Kind kind = graph.kind;
	int weight = (kind == DG || kind == UDG) ? 0 : INFINITY;
	int degree = 0;
	for (int i = 0; i < graph.vexNum; i++){//计算行
		if (graph.arcs[location][i].adj != weight){
			degree++;
		}
	}
	for (int i = 0; i < graph.vexNum; i++){//计算列
		if (graph.arcs[i][location].adj != weight){
			degree++;
		}
	}
	if (kind == UDG || kind == UDN){
		degree /= 2;
	}
	return degree;
}

//当有以下操作时,不适合 用邻接矩阵的方式来处理。
void insertVex(MGraph * graph,char  vex){
	graph->vexs[graph->vexNum++] = vex;
}
//需要移动很多元素.
void deleteVex(MGraph * graph,char  vex){
	int location = graphLocation(*graph,vex);
	//删除顶点集
	for (int i = location+1; i < graph->vexNum; i++){
		graph->vexs[i-1] = graph->vexs[i];
	}
	//计算删除的边(弧)数
	graph->arcNum -= graphDegree(*graph,vex);
	//删除边(弧)
	//vex下面的上移
	for (int i = location+1; i < graph->vexNum; i++){
		for (int j = 0; j < graph->vexNum; j++){
			graph->arcs[i-1][j] = graph->arcs[i][j];
		}
	}
	//vex右边的左移
	for (int i = location + 1; i < graph->vexNum; i++){
		for (int j = 0; j < graph->vexNum; j++){
			graph->arcs[j][i-1] = graph->arcs[j][i];
		}
	}
	//清理垃圾数据(第vexNum行 和 第vexNum列)
	int maxVexNum = graph->vexNum;
	E_Graph_Kind kind = graph->kind;
	int weight = (kind == DG || kind == UDG) ? 0 : INFINITY;
	for (int i = 0; i < maxVexNum; i++){
		graph->arcs[maxVexNum-1][i].adj = weight;
		graph->arcs[i][maxVexNum-1].adj = weight;
	}
	graph->vexNum--;
}
//插入边(弧)
void insertArc(MGraph * graph,char  vex1,char  vex2,int weight){
	int location1 = graphLocation(*graph,vex1);
	int location2 = graphLocation(*graph,vex2);
	E_Graph_Kind kind = graph->kind;
	if (kind == DG || kind == UDG){
		graph->arcs[location1][location2].adj = 1;
	}
	else{
		graph->arcs[location1][location2].adj = weight;
	}
	if (kind == UDG || kind == UDN)
	{
		graph->arcs[location2][location1].adj = graph->arcs[location1][location2].adj;
	}
}
//删除边(弧)
void deleteArc(MGraph * graph,char  vex1,char  vex2){
	int location1 = graphLocation(*graph,vex1);
	int location2 = graphLocation(*graph,vex2);
	E_Graph_Kind kind = graph->kind;
	if (kind == DG || kind == UDG){
		graph->arcs[location1][location2].adj = 0;
	}
	else{
		graph->arcs[location1][location2].adj = INFINITY;
	}
	if (kind == UDG || kind == UDN)
	{
		graph->arcs[location2][location1].adj = graph->arcs[location1][location2].adj;
	}
}

void printAdjMatrix(MGraph graph){
	for (int i = 0; i < graph.vexNum; i++){
		for (int j = 0; j < graph.vexNum; j++){
			printf("%d\t",graph.arcs[i][j]);
		}
		printf("\n");
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	MGraph graph;
	graphCreate(&graph);
	printAdjMatrix(graph);
	printf("图 有 %d个顶点,%d个边(弧)\n",graph.vexNum,graph.arcNum);
	char * isAdj = graphIsAdj(graph,'a','d')? "相邻":"不相邻";
	int degree = graphDegree(graph,'c');
	printf("a 和 d %s,c的度数是:%d\n",isAdj,degree);
	char vexFirst = firstAdjVex(graph,'c');
	char vexNext = nextAdjVex(graph,'c','a');
	printf("c的第一个邻接点是%c\nc的相对于a的下一个邻接点是%c\n",vexFirst,vexNext);
	insertVex(&graph,'e');
	printf("插入节点e之后:\n");
	printAdjMatrix(graph);
	printf("插入边(e,d)之后:\n");
	insertArc(&graph,'e','d',1);
	printAdjMatrix(graph);
	printf("删除顶点c之后:\n");
	deleteVex(&graph,'c');
	printAdjMatrix(graph);
	deleteArc(&graph,'a','b');
	printf("删除弧(a,b)之后:\n");
	printAdjMatrix(graph);
	return 0;
}

运行截图:

时间: 2024-11-06 06:42:11

看数据结构写代码(35) 图的邻接矩阵表示法的相关文章

看数据结构写代码(39) 图的遍历(深搜和广搜)

图的遍历算法 有两种 :深度优先搜索遍历 和 广度 优先搜索遍历.深度优先搜索遍历类似与 树的 先序遍历.广度优先搜索遍历类似与树的层序遍历.只不过 图 可以有 不连通的 节点,所以 得 遍历 整个顶点数组. 深搜遍历 总是 先访问当前节点的邻接点,而 广搜算法 是 先访问顶点的邻接点 要 先于 后访问顶点的邻接点 被 访问. 具体遍历顺序如下: 以下代码 以 图的 邻接多重表 为 基本结构进行 遍历. 首先更改 上节 的 查找 邻接点 和 下一个邻接点的 返回值,以及 邻接点的 代码 有误,少

看数据结构写代码(36) 图的邻接表表示与实现

图的邻接表表示法,是为每一个顶点建立一个链表,链表里存放着相同弧尾的 弧的信息,这些链表顺序存放在数组中.下面是无向图g2的邻接表 邻接表 比 邻接矩阵 节省空间,同时 也带来一些操作上的 不便,例如 看 两个顶点是否 相邻,需要 遍历 链表,在 求 无向图顶点的度时,只需 遍历 顶点的链表,而 求 有向图 顶点的度 需要 遍历 整个图 查找 弧头 为这个顶点的 个数. 如果 不想这样做,可以 建立 逆邻接表,即 链表里 存放着 相同 弧头的 弧 的信息. 下一节 要说的 十字链表 类似于这种结

看数据结构写代码(38) 图的邻接多重表表示法与实现

图的邻接多重表 是 无向图的 另一种表示法.其与 邻接表 的差别 仅仅 在于 ,邻接表 用 两个 顶点 来表示 一条边,而 邻接多重表 用一个 顶点来表示一条边.这样使得 邻接多重表 在 某些操作 要 来的 方便.例如 将 搜索过的边 做记号 或者 删除 一条边. 下面是邻接多重表的结构: 下面的 6条边 用 6个弧 节点表示,用12个指针指向,每个弧节点被 指向2次.这样使得我们 在 释放内存的时候 需要格外小心. 下面上代码: 源码工程文件网盘地址:点击打开链接 // AMLGraph.cp

看数据结构写代码(37) 图的十字链表的表示与实现

图的邻接表在 查找 有向图的 出度 很 方便,但是 在 查找 入度 时,需要遍历整个图.如果想要 方便的 查找 入度,需要 建立 逆邻接表.十字链表 正好 就是 邻接表 和 逆邻接表的集合.具体结构图如下: 感觉 十字链表 在 查找 入度时 方便 一些,其他 跟 邻接表没什么区别. 源代码 网盘地址:点击打开链接 代码如下: // CrossLinkGraph.cpp : 定义控制台应用程序的入口点. //有向图的十字链表表示法 #include "stdafx.h" #include

看数据结构写代码(42)最小生成树

首先给出 一些 概念问题: 1.生成树: 一个n个顶点的 连通图 的 极小连通子图. 它含有n个顶点,但只有 n-1条边,不存在回路. 2.最小生成树:一个带权的 无向连通图,求出 各边权值相加  最小的 生成树,叫做最小生成树. 所以 求最小生成树  首先 要满足: 1. 首先 是 无向图 2. 必须是 连通图(任意两个顶点可达)3.带权 简单的说 就是 必须是 连通网. 求 最小生成树,严蔚敏的 数据结构 给出了 两种 方法:普里姆算法和 克鲁斯卡尔算法. 普里姆算法: 克鲁斯卡尔算法: 克

看数据结构写代码(46) 关键路径

首先介绍下 概念问题: 与AOV网 相对应的 AOE网(Activity On Edge),边 表示 活动,顶点表示 事件,边的 权值表示 活动 所需的时间.AOE网 常用于求工程的 最短完成时间 以及哪些活动是影响工程进度的关键. 例如下图: v1表示 工程的 开始事件,v9表示工程的结束事件.我们将v1(入度为0)叫做源点,v9(出度为0)叫做汇点.AOE网中只有一个源点,一个汇点.否则 出现 环路,无法求出工程的最短用时. 其他顶点 表示 活动的完成,例如V2,表示 活动 a1完成,V5

看数据结构写代码(32) 赫夫曼树编码以及译码

杂谈:最近有点慵懒,不好不好.好几天都没写代码,原本准备上星期完结 树 这一章节的.现在 又耽误了.哎.要抓紧时间啊. 下面直接上代码: 可以到我的网盘下载源代码,或者 直接拷贝下面的源代码 运行 网盘地址:点击打开链接 // HuffmanTree.cpp : 定义控制台应用程序的入口点. //哈弗曼编码,译码 #include "stdafx.h" #include <stdlib.h> #include <cstring> enum E_State { E

看数据结构写代码(44) 判断无向图是否有环路

在 看 严蔚敏的 数据结构 一书 7.5小节时,书上 说" 判断有向图是否存在环要不无向图复杂.对于无向图来说,深度优先遍历过程中遇到回边(即指向已访问过的顶点的边),则必定存在环路". 看的不明白,所以 网上 百度了一下. 有了思路:故写下算法 和思路,以便以后 温故. 思路: 1.一个n个顶点,e条边的 无向图,若 e>= n,必有环路. 2.若 e < n ,需要 深度 遍历,并把 父节点传入 参数中,如果 遇到 一个 节点 被访问过 并且 不是 父节点,那么 就有环

看数据结构写代码(43) 关节点

首先 说明一下 概念问题: 关节点 :如果删除无向 图中的一个顶点,以及与顶点相关的边,把 图的 一个连通 分量 变成 两个 以上的 连通 分量.这样的顶点叫做关节点. 没有 关节点的 无向图,叫做 重连通图.重连通图中 任意 两个顶点 至少 存在 两条以上的 通路. 如果 删除 连通图上的 k个 节点,才能 破坏 他的连通性,那么 这个连通图的 连通度 为k. 下面的算法 是 求 连通图的 关节点,并没有 考虑 求图的 关节点,不过 要 改成 图的 关节点 也不难,只要 加 一个 for i