邻接表的逆邻接表

欢迎加qq群:453398542 学习讨论,会定期分享资料课程,解答问题。

/*已知有向图采用邻接表存储,设计算法,求其逆邻接表。*/
#include<stdio.h>
#include<stdlib.h>
#define max 20
typedef struct ArcNode{
	int adjvex; //该弧所指向的顶点的位置
	struct ArcNode *nextarc; //指向下一条弧的指针 
}ArcNode;
typedef struct VNode{
	char data;  //顶点信息
	ArcNode *firstarc; //指向第一条依附该顶点的弧的指针  
}VNode,AdjList[max];
typedef struct{
	AdjList vertices;
	int vexnum,arcnum;
}ALGraph;
void CreatGraph(ALGraph &G){
	int i,s,e;
	ArcNode *p;
	printf("输入结点数目和边数:");
	scanf("%d%d",&G.vexnum,&G.arcnum);
	getchar();
	printf("输入顶点信息:\n");
	for(i=1;i<=G.vexnum;i++){
		scanf("%c",&G.vertices[i].data);
		G.vertices[i].firstarc=NULL;
	} 
	getchar();
	printf("输入一条边依附的起点序号和终点序号:\n");
	for(i=1;i<=G.arcnum;i++){
		scanf("%d%d",&s,&e);
		getchar();
		p=(ArcNode *)malloc(sizeof(ArcNode));
		p->adjvex=e;
		p->nextarc=G.vertices[s].firstarc;
		G.vertices[s].firstarc=p;
	}
}
void PrintGraph(ALGraph G){
	int i;
	printf("打印有向图:\n");
	for(i=1;i<=G.vexnum;i++){
		printf("%3c",G.vertices[i].data);
		if(G.vertices[i].firstarc==NULL){
			printf("\n");
			continue;
		}
		while(G.vertices[i].firstarc){
			printf("%3d",G.vertices[i].firstarc->adjvex);
			G.vertices[i].firstarc=G.vertices[i].firstarc->nextarc;
		}
		printf("\n");
	}
}
void Reverse(ALGraph A,ALGraph &B){
	//逆邻接表
	int i,k;
	ArcNode *p1,*p2;
	B.vexnum=A.vexnum;
	B.arcnum=A.arcnum;
	for(i=1;i<=A.vexnum;i++){
		scanf("%c",&B.vertices[i].data);
		B.vertices[i].firstarc=NULL;
	} 
	for(i=1;i<=A.arcnum;i++){
		p1=A.vertices[i].firstarc;
		while(p1){
			k=p1->adjvex;
			p2=(ArcNode *)malloc(sizeof(ArcNode));
			p2->adjvex=i;
			p2->nextarc=B.vertices[k].firstarc;
			B.vertices[k].firstarc=p2;
			p1=p1->nextarc;
		}
	}
	
}
int main(){
	ALGraph G,B;
	CreatGraph(G);
	PrintGraph(G);
	Reverse(G,B);
    PrintGraph(B);
	return 0;
}

 

邻接表的一种存储方式,它利用数组和链表相结合的方式来表示邻接表存储有向和无向的方式略有不同。 首先我们来看有向邻接表。假设有n个顶点和m条边,我们可以用一个数组a来存储所有的边,a[i]表示顶点i的出边集合,每个出边用一个结构体存储,包含指向的顶点和边的权值等信息。具体实现代码如下: ``` const int N = 100010; struct Edge { int v, w; // v表示指向的顶点编号,w表示边的权值 }; Edge a[N]; // 存储所有的边 int h[N], e[N], ne[N], idx; // h表示邻接表头,e表示边集合,ne表示指向下一条边的指针,idx表示当前边的编号 void add(int a, int b, int c) { // 添加一条从a到b权值为c的边 e[idx] = b, ne[idx] = h[a], h[a] = idx ++; // 将边插入邻接表中 } int main() { int n, m; cin >> n >> m; memset(h, -1, sizeof h); // 初始化邻接表头为-1 for (int i = 0; i < m; i ++ ) { int a, b, c; cin >> a >> b >> c; add(a, b, c); // 添加一条从a到b权值为c的边 } for (int i = 1; i <= n; i ++ ) { // 输出邻接表 printf("Vertex %d: ", i); for (int j = h[i]; j != -1; j = ne[j]) { printf("-> %d(w=%d) ", e[j], a[j].w); } puts(""); } return 0; } ``` 接下来我们再看有向逆邻接表逆邻接表存储每个顶点的入边集合。具体实现代码如下: ``` const int N = 100010; struct Edge { int v, w; // v表示指向的顶点编号,w表示边的权值 }; Edge a[N]; // 存储所有的边 int rh[N], re[N], rne[N], ridx; // rh表示逆邻接表头,re表示边集合,rne表示指向下一条边的指针,ridx表示当前边的编号 void add(int a, int b, int c) { // 添加一条从a到b权值为c的边 re[ridx] = b, rne[ridx] = rh[a], rh[a] = ridx ++; // 将边插入逆邻接表中 } int main() { int n, m; cin >> n >> m; memset(rh, -1, sizeof rh); // 初始化逆邻接表头为-1 for (int i = 0; i < m; i ++ ) { int a, b, c; cin >> a >> b >> c; add(b, a, c); // 添加一条从a到b权值为c的边 } for (int i = 1; i <= n; i ++ ) { // 输出逆邻接表 printf("Vertex %d: ", i); for (int j = rh[i]; j != -1; j = rne[j]) { printf("<- %d(w=%d) ", re[j], a[j].w); } puts(""); } return 0; } ``` 接下来我们再来看无向邻接表。和有向相比,无向邻接表要多存储一份反向边。具体实现代码如下: ``` const int N = 100010; struct Edge { int v, w; // v表示指向的顶点编号,w表示边的权值 }; Edge a[N]; // 存储所有的边 int h[N], e[N], ne[N], idx; // h表示邻接表头,e表示边集合,ne表示指向下一条边的指针,idx表示当前边的编号 void add(int a, int b, int c) { // 添加一条从a到b权值为c的边 e[idx] = b, ne[idx] = h[a], h[a] = idx ++; // 将正向边插入邻接表中 e[idx] = a, ne[idx] = h[b], h[b] = idx ++; // 将反向边插入邻接表中 } int main() { int n, m; cin >> n >> m; memset(h, -1, sizeof h); // 初始化邻接表头为-1 for (int i = 0; i < m; i ++ ) { int a, b, c; cin >> a >> b >> c; add(a, b, c); // 添加一条从a到b权值为c的边 } for (int i = 1; i <= n; i ++ ) { // 输出邻接表 printf("Vertex %d: ", i); for (int j = h[i]; j != -1; j = ne[j]) { printf("-> %d(w=%d) ", e[j], a[j].w); } puts(""); } return 0; } ``` 最后我们再看无向逆邻接表。和有向相比,无向逆邻接表也要多存储一份反向边。具体实现代码如下: ``` const int N = 100010; struct Edge { int v, w; // v表示指向的顶点编号,w表示边的权值 }; Edge a[N]; // 存储所有的边 int rh[N], re[N], rne[N], ridx; // rh表示逆邻接表头,re表示边集合,rne表示指向下一条边的指针,ridx表示当前边的编号 void add(int a, int b, int c) { // 添加一条从a到b权值为c的边 re[ridx] = b, rne[ridx] = rh[a], rh[a] = ridx ++; // 将正向边插入逆邻接表中 re[ridx] = a, rne[ridx] = rh[b], rh[b] = ridx ++; // 将反向边插入逆邻接表中 } int main() { int n, m; cin >> n >> m; memset(rh, -1, sizeof rh); // 初始化逆邻接表头为-1 for (int i = 0; i < m; i ++ ) { int a, b, c; cin >> a >> b >> c; add(a, b, c); // 添加一条从a到b权值为c的边 } for (int i = 1; i <= n; i ++ ) { // 输出逆邻接表 printf("Vertex %d: ", i); for (int j = rh[i]; j != -1; j = rne[j]) { printf("<- %d(w=%d) ", re[j], a[j].w); } puts(""); } return 0; } ```
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值