图:十字链表和邻接多重表

1、十字链表代码实现:

 

  

crossLinkGraph.h

#ifndef CROSS_LINK_GRAPH_H
#define CROSS_LINK_GRAPH_H
/* 十字链表的图表示法,将邻接表和逆邻接表整合在一个顶点描述的结构上
 * 方便查找图中出度和入度
 * */
// 十字链表的边的结构
typedef struct arcBox {
	int tailVertex;				// 弧尾编号,出度的信息
	struct arcBox *tailNext;	// 下一个弧尾,下一个出度
	int headVertex;				// 弧头编号,入度的信息
	struct arcBox *headNext;	// 下一个弧头,下一个入度
	int weight;					// 弧的权重
}ArcBox;
// 十字链表的顶点结构
typedef struct {
	int no;                     //顶点的编号
	const char *show;           //图中顶点的显示数据(比如V1,V2,V3),指针指向了一个常量空间,考试时可以不用写
	ArcBox *firstIn;			// 该节点的入度
	ArcBox *firstOut;			// 该节点的出度
}CrossVertex;
// 十字链表的图结构
typedef struct {
	CrossVertex *nodes;         //顶点集
	int numVertex;              //顶点数
	int numEdge;                //边数
}CrossGraph;

// 产生n个节点的十字链表
CrossGraph *createCrossGraph(int n);
void releaseCrossGraph(CrossGraph *graph);
// 初始化图,设置节点信息
void initCrossGraph(CrossGraph *graph, int num, char *names[]);
// 添加边,从tail走向head
void addCrossArc(CrossGraph *graph, int tail, int head, int w);
// 计算no编号节点的入度
int inDegreeCrossGraph(CrossGraph *graph, int no);
// 计算no编号节点的出度
int outDegreeCrossGraph(CrossGraph *graph, int no);
#endif

crossLinkGraph.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "crossLinkGraph.h"

// 产生n个节点的十字链表
CrossGraph *createCrossGraph(int n) {
	CrossGraph *graph = (CrossGraph *) malloc(sizeof(CrossGraph));
	graph->nodes = (CrossVertex *) malloc(sizeof(CrossVertex) * n);
	graph->numVertex = n;
	graph->numEdge = 0;
	return graph;
}

//释放十字链表
void releaseCrossGraph(CrossGraph *graph) {
	int numEdges = 0;
	if (graph) {
		if (graph->nodes) {
			for (int i = 0; i < graph->numVertex; ++i) {	// 遍历所有节点,释放出度
				ArcBox *box = graph->nodes[i].firstOut;
				ArcBox *tmp; //备份思想
				while (box) {
					tmp = box;
					box = box->tailNext;
					free(tmp);
					numEdges++;
				}
			}
			printf("release %d edge!\n", numEdges);
			free(graph->nodes);
		}
		free(graph);
	}
}

 初始化图,设置节点信息
void initCrossGraph(CrossGraph *graph, int num, char **names) {
	for (int i = 0; i < num; ++i) {
		graph->nodes[i].no = i;
		graph->nodes[i].show = names[i];
		graph->nodes[i].firstOut = graph->nodes[i].firstIn = NULL;
	}
}

// 添加边,从tail走向head
void addCrossArc(CrossGraph *graph, int tail, int head, int w) {
	ArcBox *box = (ArcBox *) malloc(sizeof(ArcBox));
	box->weight = w;
	// 使用头插法,出度(先处理新节点,再处理老节点)
	box->tailVertex = tail;
	box->tailNext = graph->nodes[tail].firstOut;
	graph->nodes[tail].firstOut = box;
	// 使用头插法,入度
	box->headVertex = head;
	box->headNext = graph->nodes[head].firstIn;
	graph->nodes[head].firstIn = box;
}

// 计算no编号节点的入度
int inDegreeCrossGraph(CrossGraph *graph, int no) {
	int count = 0;
	ArcBox *box = graph->nodes[no].firstIn;
	while (box) {
		count++;
		box = box->headNext;
	}
	return count;
}

// 计算no编号节点的出度
int outDegreeCrossGraph(CrossGraph *graph, int no) {
	int count = 0;
	ArcBox *box = graph->nodes[no].firstOut;
	while (box) {
		count++;
		box = box->tailNext;
	}
	return count;
}

main.c

#include <stdio.h>
#include "crossLinkGraph.h"

static void setupGraph(CrossGraph *graph) {
	char *nodeName[] = {"V0", "V1", "V2", "V3"};
	initCrossGraph(graph, sizeof(nodeName) / sizeof(nodeName[0]), nodeName);
	addCrossArc(graph, 0, 3, 1);
	addCrossArc(graph, 1, 0, 1);
	addCrossArc(graph, 1, 2, 1);
	addCrossArc(graph, 2, 0, 1);
	addCrossArc(graph, 2, 1, 1);
}

int main() {
	int n = 4;
	CrossGraph *graph = createCrossGraph(n);
	if (graph == NULL) {
		return -1;
	}
	setupGraph(graph);
	printf("V0的入度为: %d\n", inDegreeCrossGraph(graph, 0));
	printf("V0的出度为: %d\n", outDegreeCrossGraph(graph, 0));
	releaseCrossGraph(graph);
	return 0;
}

运行结果:

2、 邻接多重表代码实现:

 

 

 

adjacencyMultiList.h

#ifndef ADJACENCY_MULTI_LIST_H
#define ADJACENCY_MULTI_LIST_H
/* 邻接多重表,使用于无向图,边的删除操作
 * 无向图如果用邻接表存储的话,一条边处理了2次,删除较为复杂
 * */

// 邻接多重表的边结构
typedef struct amlEdge {
	int iVex;				// 边的顶点i编号
	struct amlEdge *iNext;	// 顶点i编号的下一个边
	int jVex;               // 边的顶点j编号
	struct amlEdge *jNext;  // 顶点j编号的下一条边
	int weight;             // 边的权重
}MultiListEdge;

// 邻接多重表的顶点结构
typedef struct {
	int no;                     //顶点的编号
	char *show;                //图中顶点的显示数据(比如V1,V2,V3),指针指向了一个常量空间
	MultiListEdge *firstEdge; //第一条边
}MultiListVertex;

// 邻接多重表
typedef struct {
	MultiListVertex *nodes;  //顶点集
	int vertexNum;           //顶点数
	int edgeNum;             //边数
}AdjacencyMultiList;

//生成邻接多重表
AdjacencyMultiList *createMultiList(int n);
//释放邻接多重表
void releaseMultiList(AdjacencyMultiList *graph);
//初始化图的顶点集
void initMultiList(AdjacencyMultiList *graph, int n, char *names[]);
// 插入边
int insertMultiListEdge(AdjacencyMultiList *graph, int a, int b, int w);
// 删除边
int deleteMultiListEdge(AdjacencyMultiList *graph, int a, int b);
#endif

adjacencyMultiList.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "adjacencyMultiList.h"

//生成n个顶点的邻接多重表
AdjacencyMultiList *createMultiList(int n) {
	AdjacencyMultiList *multiList = (AdjacencyMultiList *) malloc(sizeof(AdjacencyMultiList));
	multiList->nodes = (MultiListVertex *) malloc(sizeof(MultiListVertex) * n);
	multiList->vertexNum = n;
	multiList->edgeNum = 0;
	return multiList;
}

//初始化图及其顶点集
void initMultiList(AdjacencyMultiList *graph, int n, char **names) {
	for (int i = 0; i < n; ++i) {
		graph->nodes[i].no = i;
		graph->nodes[i].show = names[i];
		graph->nodes[i].firstEdge = NULL;
	}
}

//插入边
int insertMultiListEdge(AdjacencyMultiList *graph, int a, int b, int w) {
	// 先产生这条边
	MultiListEdge *edge = (MultiListEdge *) malloc(sizeof(MultiListEdge));
	edge->weight = w;
	// 处理a节点的连接关系,使用头插法
	edge->iVex = a;
	edge->iNext = graph->nodes[a].firstEdge;
	graph->nodes[a].firstEdge = edge;
	// 处理b节点的连接关系,使用头插法
	edge->jVex = b;
	edge->jNext = graph->nodes[b].firstEdge;
	graph->nodes[b].firstEdge = edge;
	graph->edgeNum++; //边数加一
	return 0;
}

int deleteMultiListEdge(AdjacencyMultiList *graph, int a, int b) {
	// 找到a编号的前一个节点
	MultiListEdge *aPreEdge = NULL;                       //前置边的节点
	MultiListEdge *aCurEdge = graph->nodes[a].firstEdge; //当前节点为图中a节点的第一条边
	while (aCurEdge &&
		!((aCurEdge->iVex == a && aCurEdge->jVex == b) || (aCurEdge->jVex == a && aCurEdge->iVex == b))) {
		aPreEdge = aCurEdge;
		if (aCurEdge->iVex == a) {
			aCurEdge = aCurEdge->iNext;
		} else {
			aCurEdge = aCurEdge->jNext;
		}
	}
	if (aCurEdge == NULL)
		return -1; //当前节点为空时,没找到,不用删,返回-1

	// 找到b编号的前一个节点
	MultiListEdge *bPreEdge = NULL;
	MultiListEdge *bCurEdge = graph->nodes[b].firstEdge;
	while (bCurEdge &&
		!((bCurEdge->iVex == a && bCurEdge->jVex == b) || (bCurEdge->jVex == a && bCurEdge->iVex == b))) {
		bPreEdge = bCurEdge;
		if (bCurEdge->iVex == b) {
			bCurEdge = bCurEdge->iNext;
		} else {
			bCurEdge = bCurEdge->jNext;
		}
	}
	if (bCurEdge == NULL)
		return -1;

	// 再删除
	// 先处理a顶点的关系
	if (aPreEdge == NULL) {			// 前置边的节点为空,是从头节点处开始删除,即是第一条边
		if (aCurEdge->iVex == a) {  // 假如是从i方向找到a的
			graph->nodes[a].firstEdge = aCurEdge->iNext;
		} else {                    // 假如是从j方向找到a的
			graph->nodes[a].firstEdge = aCurEdge->jNext;
		}
	} else {  //总共四种情况
		if (aPreEdge->iVex == a && aCurEdge->iVex == a) { //Pre和Cur都是i方向
			aPreEdge->iNext = aCurEdge->iNext;
        }else if(aPreEdge->iVex == a && aCurEdge->jVex == a){ //Pre是i方向,Cur是j方向
            aPreEdge->iNext = aCurEdge->jNext;
        }else if(aPreEdge->jVex == a && aCurEdge->iVex == a){ //Pre是j方向,Cur是i方向
            aPreEdge->jNext = aCurEdge->iNext;
        }else{  //Pre和Cur都是j方向
            aPreEdge->jNext = aCurEdge->jNext;
        }
	}
	// 再处理b顶点的关系
	if (bPreEdge == NULL) {
		if (bCurEdge->iVex == b) {
			graph->nodes[b].firstEdge = bCurEdge->iNext;
		} else {
			graph->nodes[b].firstEdge = bCurEdge->jNext;
		}
	} else {
		if (bPreEdge->iVex == b && bCurEdge->iVex == b) { 
			bPreEdge->iNext = bCurEdge->iNext;
        }else if(bPreEdge->iVex == b && bCurEdge->jVex == b){
            bPreEdge->iNext = bCurEdge->jNext;
        }else if(bPreEdge->jVex == b && bCurEdge->iVex == b){
            bPreEdge->jNext = bCurEdge->iNext;
        }else{
            bPreEdge->jNext = bCurEdge->jNext;
        }
	}
	// 释放cur
	free(aCurEdge);
	graph->edgeNum--; //边数减一
	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用: 十字链表是一种用于示有向的数据结构。它包含两种类型的节点:顶点节点和弧节点。顶点节点记录着当前顶点的相关信息,如顶点的数据、指向该顶点的入度弧节点和指出该顶点的出度弧节点。而弧节点则示两个顶点之间的有向边关系,其中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 ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值