【数据结构】邻接多重表

邻接多重表

上一节总结了有向图的另外一种链式存储结构—十字链表,该节继续总结无向图的另一种链式存储结构。

基本概念

邻接表虽然已经能够很好地表示无向图了,但是无向图访问或者删除一条边(Vi,Vj)时需要同时访问两个链表i和j并分别找到对应的边结点,这给针对图的边的操作(标记或删除)带来不便利。邻接多重表因此而演变过来的。

邻接多重表中,无向图中的每一个顶点分配一个顶点结点,所有顶点结点构成一个顶点数组adjmultiList[num]。另外每条边也分配一个边结点。

顶点结构如下所示:


其中data用来记录顶点的信息,firstEdge用来表示依附于该顶点的第一条边。

顶点数组如下所示:

边结点结构如下所示:

其中mark表示标志位,用于标记该边是否已经被访问过;iVex和jVex表示该边的两个顶点在顶点数组adjmultiList[num]中的位置;iLink和jLink分别表示指向依附于顶点iVex和jVex下一条边的指针。

从上面的顶点和边的结构来看,邻接多重表和邻接表的主要区别在于:邻接多重表中的边结点同时被链入对应边顶点的链表内(2条);邻接表中的边用两个表结点表示。另外,邻接多重表中的边结点就表示一条边,比较容易理解。

举个例子。某无向图如下图所示:


当采用邻接表表示该无向图时,其邻接表入下图所示:

如上图所示,图中黄色标记的结点表示A-D之间的边,在邻接表中一条边需要两个结点表示。因此如果对于边的操作(标记或者删除)则需要访问两条链表。

当采用邻接多重表表示该无向图时,其邻接多重表入下图所示:


如上图所示,结点A-D 之间的边,在邻接多重表中只需要一个边结点既可以表示。另外,在该结构中,每个边结点被链入了两个不同的链表。其中A-D之间的边被链入了红色和绿色标记的链表中。如果需要访问一条边,则可以从该边的两个顶点结点中的任何一个出发,遍历依附于该顶点的边构成的链表即可。如果需要删除一条边,则只需要删除一个边结点,但是需要修改这条边依附的两个顶点所对应的链表。另外,需要注意的是,在无向图中,边结点中的iVexjVex链域与该边所依附的顶点无关,即iVex=0jVex=3iVex=3jVex=0这都表示同一条边A-D,因此这给链表的指针修改带来一定的麻烦。

基本操作

1、建立无向图的邻接多重表

输入ABCDE五个顶点V={A,B,C,D,E},然后输入边E={(A,B),(B,C),(B,E),(C,D),(C,E),(D,A)},建立如下无向图:


代码如下:

Status createAMLGraph(AMLGraph &G, int vexNum){
	G.vexNum = vexNum;
	G.edgeNum = 0;
	for(int i = 0; i< G.vexNum; i++){
		cin>>G.adjmultiList[i].data;
		G.adjmultiList[i].firstEdge = NULL;
	}

	cout<<"Try to input arcs info"<<endl;
	while(1){
		char SWITCH;
		cout<<"are you going to add a edge into AMLGraph?(y?n)"<<endl;
		
		cin>>SWITCH;
		if(SWITCH == 'y'){
			cout<<"please input two nodes of "<<G.edgeNum+1<<"-th arc"<<endl;

			vertexNode node1, node2;
			cin>>node1.data>>node2.data;
			insertEdge(G, node1, node2);
			G.edgeNum++;
		}
		else
			break;
	}
	return 1;
}

2、打印无向图的邻接多重表


代码如下:

Status printAMLGraph(AMLGraph &G){
	for(int i = 0; i<G.vexNum; i++){
		cout<<i<<" "<<G.adjmultiList[i].data;
		edgeNode *edge = G.adjmultiList[i].firstEdge;

		while(edge){
			cout<<"-->|"<<edge->iVex<<"|"<<edge->jVex<<"|";
			if(edge->iVex == i){
				edge = edge->iLink;
			}
			else{
				edge = edge->jLink;
			}
		}
		cout<<"-->NULL"<<endl;
	}
	return 1;
}

3、向无向图中添加顶点

向无向图中添加结点F。


代码如下:

Status insertNode(AMLGraph &G, vertexNode node){
	G.adjmultiList[G.vexNum].data = node.data;
	G.adjmultiList[G.vexNum].firstEdge = NULL;
	G.vexNum++;
	return 1;
}

4、向无向图中添加边

向无向图中添加边A-C。


代码如下:
void insertEdgeAction(AMLGraph &G, int index1, int index2){
	edgeNode *p, *q;
	edgeNode *edge = new edgeNode[1];
	
	edge->iVex = index1;
	edge->jVex = index2;

	p = G.adjmultiList[index1].firstEdge;//相当于链表的插入
	if(!p){
		G.adjmultiList[index1].firstEdge = edge;
		edge->iLink = NULL;
	}
	else{
		G.adjmultiList[index1].firstEdge = edge;

		edge->iLink = p;
	}

	q = G.adjmultiList[index2].firstEdge;//相当于链表的插入
	if(!q){
		G.adjmultiList[index2].firstEdge = edge;
		edge->jLink = NULL;
	}
	else{
		G.adjmultiList[index2].firstEdge = edge;
		edge->jLink = q;
	}
}

Status insertEdge(AMLGraph &G, vertexNode node1, vertexNode node2){
	int index1 = locateNode(G, node1);//i和j表示AdjList[MAX_VERTEX_NUM]中的位置
	int index2 = locateNode(G, node2);
	if(index1<0 || index2<0) exit(-1);
	
	insertEdgeAction(G, index1, index2);

	return 1;
}

5、删除无向图中的边

删除无线图中的边B-C。


代码如下:
Status deleteEdgeAction(AMLGraph &G, int index1, int index2){
	edgeNode *curEdge = G.adjmultiList[index1].firstEdge;
	edgeNode *preEdge = curEdge;
	int count = 0;

	if(!curEdge) return 1;
	else{
		while(curEdge){
			count++;
			if((curEdge->iVex == index1&&curEdge->jVex == index2)||(curEdge->iVex == index2&&curEdge->jVex == index1)){
				break;
			}
			preEdge = curEdge;
			if(curEdge->iVex ==index1)
				curEdge = curEdge->iLink;
			else
				curEdge = curEdge->jLink;
		}
	}

	if(!curEdge)
		return 1;
	else if(count<=1){
		if(preEdge->iVex == index1)
			G.adjmultiList[index1].firstEdge = preEdge->iLink;
		else 
			G.adjmultiList[index1].firstEdge = preEdge->jLink;
	}
	else{
		if(preEdge->iVex == index1){ 
			if (curEdge->iVex == index1)
				preEdge->iLink = curEdge->iLink;
			else
				preEdge->iLink = curEdge->jLink;
		}
		else{
			if (curEdge->iVex == index1)
				preEdge->jLink = curEdge->iLink;
			else
				preEdge->jLink = curEdge->jLink;
		}
	}
	return 1;
}

Status deleteEdge(AMLGraph &G, vertexNode node1, vertexNode node2){
	int index1 = locateNode(G, node1);
	int index2 = locateNode(G, node2);

	deleteEdgeAction(G, index1, index2);
	deleteEdgeAction(G, index2, index1);

	return 1;
}

6、删除无向图中的顶点

删除结点C。


代码如下:

Status deleteNode(AMLGraph &G, vertexNode node){
	//删除结点时,结点不真实删除而只是删除结点相关的边以及赋值该结点的data为特殊值‘0’
	int index = locateNode(G, node);

	for(int i = 0; i<G.vexNum; i++){
		if(i == index) continue;
		else{
			deleteEdge(G, G.adjmultiList[index], G.adjmultiList[i]);
		}
	}
	G.adjmultiList[index].firstEdge = NULL;
	G.adjmultiList[index].data = '0';
	return 1;
}

7、全部代码:

// 邻接多重表.cpp : Defines the entry point for the console application.
//

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

using namespace std;

#define MAX_VERTEX_NUM 20

typedef int infoType;
typedef char vertexType;
typedef int Status;

typedef struct edgeNode{
	int iVex, jVex;
	struct edgeNode *iLink, *jLink;
	infoType *info;
}edgeNode;

typedef struct vertexNode{
	vertexType data;
	edgeNode *firstEdge;
}vertexNode;

typedef struct{
	vertexNode adjmultiList[MAX_VERTEX_NUM];
	int vexNum, edgeNum;
}AMLGraph;

int locateNode(AMLGraph &G, vertexNode node){
	for(int i=0; i<G.vexNum;i++){
		if(node.data == G.adjmultiList[i].data)
			return i;
	}
	return -1;
}

Status insertEdge(AMLGraph &G, vertexNode node1, vertexNode node2);

Status createAMLGraph(AMLGraph &G, int vexNum){
	G.vexNum = vexNum;
	G.edgeNum = 0;
	for(int i = 0; i< G.vexNum; i++){
		cin>>G.adjmultiList[i].data;
		G.adjmultiList[i].firstEdge = NULL;
	}

	cout<<"Try to input arcs info"<<endl;
	while(1){
		char SWITCH;
		cout<<"are you going to add a edge into AMLGraph?(y?n)"<<endl;
		
		cin>>SWITCH;
		if(SWITCH == 'y'){
			cout<<"please input two nodes of "<<G.edgeNum+1<<"-th arc"<<endl;

			vertexNode node1, node2;
			cin>>node1.data>>node2.data;
			insertEdge(G, node1, node2);
			G.edgeNum++;
		}
		else
			break;
	}
	return 1;
}

void insertEdgeAction(AMLGraph &G, int index1, int index2){
	edgeNode *p, *q;
	edgeNode *edge = new edgeNode[1];
	
	edge->iVex = index1;
	edge->jVex = index2;

	p = G.adjmultiList[index1].firstEdge;//相当于链表的插入
	if(!p){
		G.adjmultiList[index1].firstEdge = edge;
		edge->iLink = NULL;
	}
	else{
		G.adjmultiList[index1].firstEdge = edge;

		edge->iLink = p;
	}

	q = G.adjmultiList[index2].firstEdge;//相当于链表的插入
	if(!q){
		G.adjmultiList[index2].firstEdge = edge;
		edge->jLink = NULL;
	}
	else{
		G.adjmultiList[index2].firstEdge = edge;
		edge->jLink = q;
	}
}

Status insertEdge(AMLGraph &G, vertexNode node1, vertexNode node2){
	int index1 = locateNode(G, node1);//i和j表示AdjList[MAX_VERTEX_NUM]中的位置
	int index2 = locateNode(G, node2);
	if(index1<0 || index2<0) exit(-1);
	
	insertEdgeAction(G, index1, index2);

	return 1;
}

Status printAMLGraph(AMLGraph &G){
	for(int i = 0; i<G.vexNum; i++){
		cout<<i<<" "<<G.adjmultiList[i].data;
		edgeNode *edge = G.adjmultiList[i].firstEdge;

		while(edge){
			cout<<"-->|"<<edge->iVex<<"|"<<edge->jVex<<"|";
			if(edge->iVex == i){
				edge = edge->iLink;
			}
			else{
				edge = edge->jLink;
			}
		}
		cout<<"-->NULL"<<endl;
	}
	return 1;
}

Status insertNode(AMLGraph &G, vertexNode node){
	G.adjmultiList[G.vexNum].data = node.data;
	G.adjmultiList[G.vexNum].firstEdge = NULL;
	G.vexNum++;
	return 1;
}

Status deleteEdgeAction(AMLGraph &G, int index1, int index2){
	edgeNode *curEdge = G.adjmultiList[index1].firstEdge;
	edgeNode *preEdge = curEdge;
	int count = 0;

	if(!curEdge) return 1;
	else{
		while(curEdge){
			count++;
			if((curEdge->iVex == index1&&curEdge->jVex == index2)||(curEdge->iVex == index2&&curEdge->jVex == index1)){
				break;
			}
			preEdge = curEdge;
			if(curEdge->iVex ==index1)
				curEdge = curEdge->iLink;
			else
				curEdge = curEdge->jLink;
		}
	}

	if(!curEdge)
		return 1;
	else if(count<=1){
		if(preEdge->iVex == index1)
			G.adjmultiList[index1].firstEdge = preEdge->iLink;
		else 
			G.adjmultiList[index1].firstEdge = preEdge->jLink;
	}
	else{
		if(preEdge->iVex == index1){ 
			if (curEdge->iVex == index1)
				preEdge->iLink = curEdge->iLink;
			else
				preEdge->iLink = curEdge->jLink;
		}
		else{
			if (curEdge->iVex == index1)
				preEdge->jLink = curEdge->iLink;
			else
				preEdge->jLink = curEdge->jLink;
		}
	}
	return 1;
}

Status deleteEdge(AMLGraph &G, vertexNode node1, vertexNode node2){
	int index1 = locateNode(G, node1);
	int index2 = locateNode(G, node2);

	deleteEdgeAction(G, index1, index2);
	deleteEdgeAction(G, index2, index1);

	return 1;
}

Status deleteNode(AMLGraph &G, vertexNode node){
	//删除结点时,结点不真实删除而只是删除结点相关的边以及赋值该结点的data为特殊值‘0’
	int index = locateNode(G, node);

	for(int i = 0; i<G.vexNum; i++){
		if(i == index) continue;
		else{
			deleteEdge(G, G.adjmultiList[index], G.adjmultiList[i]);
		}
	}
	G.adjmultiList[index].firstEdge = NULL;
	G.adjmultiList[index].data = '0';
	return 1;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int vexNum = 5;
	AMLGraph G;
	cout<<"Try to create a adjacence multilist of a graph ..."<<endl;
	createAMLGraph(G, vexNum);
	printAMLGraph(G);

	cout<<"Try to insert a node into the graph..."<<endl;
	vertexNode node;
	cout<<"   please input the data of the node to insert:"<<endl;
	cin>>node.data;
	insertNode(G, node);
	printAMLGraph(G);

	cout<<"Try to insert a edge into the graph..."<<endl;
	vertexNode node1, node2;
	cout<<"   please choose two node:"<<endl;
	cin>>node1.data>>node2.data;
	insertEdge(G, node1, node2);
	printAMLGraph(G);

	cout<<"Try to delete the edge between two nodes..."<<endl;
	vertexNode node3, node4;
	cout<<"   please choose a edge with specifing two nodes:"<<endl;
	cin>>node3.data>>node4.data;
	deleteEdge(G, node3, node4);
	printAMLGraph(G);

	cout<<"Try to delete a node of the graph..."<<endl;
	vertexNode node5;
	cout<<"   please choose a node:"<<endl;
	cin>>node5.data;
	deleteNode(G, node5);
	printAMLGraph(G);

	system("pause");
	return 0;
}



  • 52
    点赞
  • 123
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
对任意给定的(顶点数不小于20,边数不少于30,的类型可以是有向、无向、有向网、无向网),能够输入的顶点和边(或弧)的信息,并存储到相应存储结构(邻接矩阵、邻接表、十字链表、邻接多重表,任选其中两种类型),对自己所创建的完成以下操作: 对无向求每个顶点的度,或对有向求每个顶点的入度和出度(5分) 完成插入顶点和边(或弧)的功能(5分) 完成删除顶点和边(或弧)的功能(5分) 两种存储结构的转换(5分),如果其中一种存储结构为十字链表或邻接多重表则增加5分。 输出的深度优先遍历序列或广度优先遍历序列(5分) 求的深度优先或广度优先的生成树(或生成森林)(存储结构为孩子-兄弟链表),并对生成树进行遍历(15分) 判断的连通性,输出连通分量的个数(5分) 判断中是否存在环,无向5分,有向10分 给出顶点u和v,判断u到v是否存在路径(5分) 10、求顶点u到v的一条简单路径(10分) 11、求顶点u到v的所有简单路径(15分) 12、求顶点u到v的最短路径(10分) 13、求顶点u到其余各顶点的最短路径(15分) 14、求任两个顶点之间的最短路径(15分) 15、求最小生成树(15分) 16、对于有一个源点和一个汇点的有向网,求关键路径(20分) 编程环境可以是C、VC++、JAVA,每位同学从上述题目中选择100分的题目,注意,必须选择第1-6题。
引用: 十字链表是一种用于表示有向数据结构。它包含两种类型的节点:顶点节点和弧节点。顶点节点记录着当前顶点的相关信息,如顶点的数据、指向该顶点的入度弧节点和指出该顶点的出度弧节点。而弧节点则表示两个顶点之间的有向边关系,其中tailvex表示弧的起始顶点,headvex表示弧的结束顶点,而headlink和taillink分别指向与该节点相邻的下一条弧的节点。 引用: 邻接多重表也是一种用于表示无向数据结构。它与十字链表相似,同样包含顶点节点和边节点。每个顶点节点记录着当前顶点的相关信息,如顶点的数据以及与该顶点相邻的一条边的节点。而边节点则表示两个顶点之间的无向边关系,其中ivex和jvex分别表示边的两个顶点,ilink和jlink分别指向与该边相邻的下一条边的节点。需要注意的是,邻接多重表中的边是双向的,因此在的存储中每条边都需要用两个边节点表示。 所以,十字链表是用于表示有向数据结构,而邻接多重表则用于表示无向数据结构。它们分别在记录顶点和边之间的关系上有所不同。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [6-4-十字链表与邻接多重表](https://blog.csdn.net/weixin_45825865/article/details/116152711)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bible_reader

如果觉得文章有用,欢迎打赏支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值