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

图的邻接多重表 是 无向图的 另一种表示法。其与 邻接表 的差别 仅仅 在于 ,邻接表 用 两个 顶点 来表示 一条边,而 邻接多重表 用一个 顶点来表示一条边。这样使得 邻接多重表 在 某些操作 要 来的 方便。例如 将 搜索过的边 做记号 或者 删除 一条边。

下面是邻接多重表的结构:

下面的 6条边 用 6个弧 节点表示,用12个指针指向,每个弧节点被 指向2次。这样使得我们 在 释放内存的时候 需要格外小心。

下面上代码:

源码工程文件网盘地址:点击打开链接

// AMLGraph.cpp : 定义控制台应用程序的入口点。
//无向图的邻接多重表

#include "stdafx.h"
#include <cstdlib>

#define MAX_VEX_NUM 20
enum E_State
{
	E_State_Error = 0,
	E_State_Ok = 1,
};
enum E_VisitIf
{
	unvisited = 0,
	visited = 1,
};
struct ArcNode
{
	E_VisitIf mark;
	int iIndex,jIndex;//顶点i,j在图中的位置
	ArcNode * iNext;//与i顶点点相关的下一个弧
	ArcNode * jNext;//与j顶点点相关的下一个弧
};

struct VNode
{
	char vexName;
	ArcNode * head;//头指针
};

struct AMLGraph
{
	VNode adjMuList[MAX_VEX_NUM];//顶点数组
	int vexNum,arcNum;
};

//获取弧 的 头节点
ArcNode * getHeadNode(){
	ArcNode * pNode = (ArcNode *)malloc(sizeof(ArcNode));
	if (pNode){
		pNode->iIndex = pNode->jIndex = -1;
		pNode->iNext = pNode->jNext = NULL;
		pNode->mark = unvisited;
	}
	return pNode;
}

ArcNode * getArcNode(int iIndex,int jIndex){
	ArcNode * pNode = getHeadNode();
	if (pNode){
		pNode->iIndex = iIndex;
		pNode->jIndex = jIndex;
	}
	return pNode;
}

int vexLocation(AMLGraph g,char vex){
	for (int i = 0; i < g.vexNum; i++){
		if (g.adjMuList[i].vexName == vex){
			return i;
		}
	}
	return -1;
}

void createGrahp(AMLGraph * g){
	printf("输入图的顶点数 和 边(弧)数\n");
	scanf("%d%d%*c",&g->vexNum,&g->arcNum);
    //构造顶点集
    printf("请输入顶点集\n");
    for (int i = 0; i < g->vexNum; i++){
        char name;
		scanf("%c",&name);
		g->adjMuList[i].vexName = name;
		g->adjMuList[i].head = getHeadNode();//建立 头节点,并让头指针指向头节点
    }
    //构造顶点关系
    fflush(stdin);
    printf("请输入顶点的关系\n");
    for (int i = 0; i < g->arcNum; i++){
        char vex1,vex2;
		scanf("%c%c%*c",&vex1,&vex2);
		int location1 = vexLocation(*g,vex1);
		int location2 = vexLocation(*g,vex2);
		ArcNode * pNode = getArcNode(location1,location2);
		pNode->iNext = g->adjMuList[location1].head->iNext;
		g->adjMuList[location1].head->iNext = pNode;
		pNode->jNext = g->adjMuList[location2].head->iNext;
		g->adjMuList[location2].head->iNext = pNode;
    }
}

void destoryGraph(AMLGraph * g){
	for (int i = 0; i < g->vexNum; i++){
		ArcNode * next = g->adjMuList[i].head->iNext;
		while (next != NULL){
			ArcNode * freeNode = next;
			next = next->iIndex == i ? next->iNext : next->jNext;
			if (freeNode->iIndex == i){////只释放 iIndex 等于 i的节点,要不会多次释放
				free(freeNode);
			}
		}
		free(g->adjMuList[i].head);
		g->adjMuList[i].head = NULL;
		g->adjMuList[i].vexName = ' ';
		g->vexNum = g->arcNum = 0;
	}
}

//顶点vex1 和顶点vex2 是否相邻
bool graphIsAdj(AMLGraph g,char vex1,char vex2){
	int location = vexLocation(g,vex1);
	ArcNode * next = g.adjMuList[location].head->iNext;
	while (next != NULL){
		if (g.adjMuList[next->iIndex].vexName == vex2 || g.adjMuList[next->jIndex].vexName == vex2){
			return true;
		}
		next = next->iIndex == location ? next->iNext : next->jNext;
	}
	return false;
}

int graphDegree(AMLGraph g,char vex){
	int degree = 0;
	int location = vexLocation(g,vex);
	ArcNode * next = g.adjMuList[location].head->iNext;//计算所有出度
	while (next != NULL){
		degree++;
		next = next->iIndex == location ? next->iNext : next->jNext;
	}
	return degree;
}

char firstAdj(AMLGraph g,char vex){
	int location = vexLocation(g,vex);
	ArcNode * next = g.adjMuList[location].head->iNext;
	if (next != NULL)
	{
		int index = next->iIndex == location ? next->jIndex : next->iIndex;
		return g.adjMuList[index].vexName;
	}
	return ' ';
}

char nextAdj(AMLGraph g,char vex1,char vex2){
	int location = vexLocation(g,vex1);
	ArcNode * next = g.adjMuList[location].head->iNext;
	while (next != NULL){//查找到 vex2
		char iName = g.adjMuList[next->iIndex].vexName;
		char jName = g.adjMuList[next->jIndex].vexName;
		if (iName == vex2 || jName == vex2){
			next = next->iIndex == location ? next->iNext : next->jNext;
			break;
		}
	}
	if (next != NULL){
		int index = next->iIndex == location ? next->jIndex : next->iIndex;
		return g.adjMuList[index].vexName;
	}
	return ' ';
}
//插入边(弧)
void insertArc(AMLGraph * g,char vex1,char vex2){
	int location1 = vexLocation(*g,vex1);
	int location2 = vexLocation(*g,vex2);
	ArcNode * node = getArcNode(location1,location2);
	node->iNext = g->adjMuList[location1].head->iNext;
	g->adjMuList[location1].head->iNext = node;
	node->jNext = g->adjMuList[location2].head->iNext;
	g->adjMuList[location2].head->iNext = node;
	g->arcNum ++;
}
//删除边(弧)
void deleteArc(AMLGraph * g,char vex1,char vex2){
	g->arcNum--;
	int location1 = vexLocation(*g,vex1);
	int location2 = vexLocation(*g,vex2);
	ArcNode * next = g->adjMuList[location1].head->iNext;
	ArcNode * pre = g->adjMuList[location1].head;
	while (next != NULL){
		if (next->iIndex == location2){
			if (pre == g->adjMuList[location1].head || pre->iIndex == location1){//删除的是第一个节点.或者 前驱的index = location1
				pre->iNext = next->jNext;
			}
			else{
				pre->jNext = next->jNext;
			}
			break;
		}
		else if(next->jIndex == location2){
			if (pre == g->adjMuList[location1].head || pre->iIndex == location1){//删除的是第一个节点.或者 前驱的index = location1
				pre->iNext = next->iNext;
			}
			else{
				pre->jNext = next->iNext;
			}
			break;
		}
		pre = next;
		next = next->iIndex == location1 ? next->iNext : next->jNext;
	}
	next = g->adjMuList[location2].head->iNext;
	pre = g->adjMuList[location2].head;
	while (next != NULL){
		if (next->iIndex == location1){
			if (pre == g->adjMuList[location2].head || pre->iIndex == location2){//删除的是第一个节点.或者 前驱的index = location1
				pre->iNext = next->jNext;
			}
			else{
				pre->jNext = next->jNext;
			}
			free(next);
			break;
		}
		else if(next->jIndex == location1){
			if (pre == g->adjMuList[location2].head || pre->iIndex == location2){//删除的是第一个节点.或者 前驱的index = location1
				pre->iNext = next->iNext;
			}
			else{
				pre->jNext = next->iNext;
			}
			free(next);
			break;
		}
		pre = next;
		next = next->iIndex == location2 ? next->iNext : next->jNext;
	}
}
//插入顶点
void insertVex(AMLGraph * g, char vex){
	if (g->vexNum < MAX_VEX_NUM){
		g->adjMuList[g->vexNum].vexName = vex;
		g->adjMuList[g->vexNum].head = getHeadNode();
		g->vexNum++;
	}
}
//删除顶点
void deleteVex(AMLGraph * g,char vex){
	int location = vexLocation(*g,vex);
	//删除顶点 同样需要 遍历整个 图 查找 与 vex 相关的弧节点
	for (int i = 0; i < g->vexNum; i++){
		ArcNode * next = g->adjMuList[i].head->iNext;
		while (next != NULL){
			if (next->iIndex == location || next->jIndex == location){
				ArcNode * delNode = next;
				next = next->iIndex == location ? next->iNext : next->jNext;
				char delData1 = g->adjMuList[delNode->iIndex].vexName;
				char delData2 = g->adjMuList[delNode->jIndex].vexName;
				deleteArc(g,delData1,delData2);
			}
			else{
				next = next->iIndex == location ? next->iNext : next->jNext;
			}
		}
	}
	//更改因删除顶点 而导致的元素位置变化..
	for (int i = 0; i < g->vexNum; i++){
		ArcNode * next = g->adjMuList[i].head->iNext;
		while (next != NULL){
			if (next->iIndex == i){
				if(next->iIndex > location){
				next->iIndex --;
				}
				if(next->jIndex > location){
					next->jIndex --;
				}
			}
			next = next->iIndex == location ? next->iNext : next->jNext;
		}
	}
	free(g->adjMuList[location].head);//释放头节点
	//vex下面的 顶点上移
	for (int i = location + 1; i < g->vexNum; i++){
		g->adjMuList[i-1] = g->adjMuList[i];
	}
	g->vexNum --;
}

void printGrahp(AMLGraph g){
	for (int i = 0; i < g.vexNum; i++){
		printf("%c的 邻接点有:",g.adjMuList[i].vexName);
		ArcNode * next = g.adjMuList[i].head->iNext;//删除所有弧尾
		while (next != NULL){
			int index = next->iIndex == i ? next->jIndex : next->iIndex;
			printf("%c",g.adjMuList[index].vexName);
			next = next->iIndex == i ? next->iNext : next->jNext;
		}
		printf("\n");
	}
}
//邻接多重表
int _tmain(int argc, _TCHAR* argv[])
{
	AMLGraph g;
	createGrahp(&g);
	printGrahp(g);
	printf("图的顶点数:%d,边(弧)树为:%d\n",g.vexNum,g.arcNum);
	char * isAdj = graphIsAdj(g,'b','d')? "相邻" : "不相邻";
	int degree = graphDegree(g,'d');
	char first = firstAdj(g,'c');
	char next = nextAdj(g,'d','c');
	printf("c的第一个邻接点是%c,d的c邻接点的下一个邻接点是:%c\n",first,next);
	printf("b 和 d %s,d的度为:%d\n",isAdj,degree);
	insertVex(&g,'f');
	printf("插入f顶点之后图结构如下:\n");
	printGrahp(g);
	insertArc(&g,'e','f');
	printf("插入(e,f) 之后图结构如下:\n");
	printGrahp(g);
	deleteArc(&g,'d','c');
	printf("删除(d,c)之后图结构如下:\n");
	printGrahp(g);
	deleteVex(&g,'c');
	printf("删除顶点c之后图结构如下:\n");
	printGrahp(g);
	printf("图的顶点数:%d,边(弧)数为:%d\n",g.vexNum,g.arcNum);
	destoryGraph(&g);
	return 0;
}

运行截图:

时间: 2024-10-14 07:34:35

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

看数据结构写代码(61) 哈希表

前面说的 各种查找都是 基于 "比较" 的基础 来进行 查找的.查找的 效率 要 看 比较的 次数.那么 有没有 不需要 比较,就可以 找到 想要的数据的 方法呢? 哈希表 就是 这样的 一种方法,它用  数组 作为 保存 关键字的 数据原型,通过 一个 哈希 函数f(k),来找到 关键字 存储的位置,从而 找到想要的信息. 例如 我们 想要解决 这样的一个问题: 假设这有一个各种字母组成的字符串,假设这还有另外一个字符串,而且这个字符串里的字母数相对少一些.什么方法能最快的查出所有小

看数据结构写代码(53) 静态查找表(线性查找,二分查找,斐波那契查找,插值查找)

查找定义:根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录). 查找表分类:静态查找表和动态查找表. 静态查找表:只查找,而不进行插入,删除. 动态查找表:在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素. 静态表的 查找 大致 四种 算法: 线性查找,二分查找,斐波那契查找和插值查找. 其中 在线性查找之前,对表 无要求.对于 其余三种 需要 在查找之前 排序.插值查找 除了 需要 排序,还需要 均匀分布. 下面 给出代码: 线性查

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

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

看数据结构写代码(40) 无向图的深度优先生成树与广度优先生成树

图的深度优先遍历 和 广度 优先 遍历 算法中的 每一次 最外层 循环 都 产生 一个 无向图 的 连通分量,每一个连通分量,都可以产生一个生成树,将这些生成树合在 一起 就是 一个 森林. 用 树的 孩子 兄弟 链表 表示法 来 表示 这个 森林, 就是 这一节 算法的  内容. 深度优先森林 代码 : //深度优先生成森林 void dfsTree(AMLGraph g,int i,Tree * t,bool isVisited[]){ isVisited[i] = true; bool i

7-4-无向图的邻接多重表存储结构-图-第7章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第7章  图 - 无向图的邻接多重表存储结构 ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? Status.h.Scanf.c.LinkQueue.c      

[数据结构]图,邻接多重表,十字链表

十字链表 你会发现,要表示一个有向图,因为有 出度 和 入度 ,需要两个邻接表:邻接表和逆邻接表. 其实我们可以把这两个表整合在一起,也就是十字链表(Orthogonal List). 我们依然需要构造一种结构体A,用结构体A的数组来存放所有顶点-我们其实可以把它叫做 顶点表. 我们构造的结构体A如下: data firstin firstout 构造结构体B,用结构体B来记录与这个顶点 用边邻接的 顶点的相关信息,我们把它叫做 边表. tailvex headvex headlink tail

图的邻接多重表和搜索(C++版本)

最近在学数据结构,学到图这一章,网上的C++版本的代码乱得不行,所以自己写了一个完整C++版本的放这里. 用邻接多重表表示一个无向图,并给出DFS和BFS搜索代码.邻接多重表好处就是贼直观,几条边就几个边表的元素. 代码如下: 边表节点定义(其实就是边的定义) typedef struct EdgeNode //邻接多重表 { int iVertex; EdgeNode* iLink; int jVertex; EdgeNode* jLink; }; 顶点表节点的定义 template <typ

图的邻接多重表

问题引出:无向图中,例如A0-A1这条边,如红色框所示,其实他们说明的是同一个信息,但是开了两个空间就浪费了 于是考虑用一个方向来明确出一条边,邻接多重表是无向图的另一种链式存储结构,它与十字链表非常类似 例如:从A0出发有边0-1,边3-0,边0-2 ,注意空所放的位置,是对应节点的后面如图中红色框所示,0-1边中的黑色空只能放在1后面不能放在0后面,因为它代表的是从1出来的边 原文地址:https://www.cnblogs.com/Liu269393/p/10226022.html

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

杂谈:最近清明小长假,好好的放松了一下.节前 和 节后 都有点 松懈.不好,不好.贵在坚持.加油. 图的邻接矩阵表示法是用 两个数组 来表示 图的数据结构.一个是顶点数组,另一个是邻接矩阵数组.邻接矩阵 里存放着 顶点的关系. 用邻接矩阵表示图,在 看 顶点之间 是否有边,或者 求顶点的度等操作时比较简单.但空间浪费巨大,在插入,删除 顶点 和边 操作时 需要 移动大量数据,造成不便.所以在插入删除比较多,节点数比较多的时候 不宜 使用这种结构. 下面上代码: 源代码网盘地址:点击打开链接 //