图的邻接表表示及其基本操作

1.邻接表

邻接表是一种链式存储结构。它用n个带头结点的单链表代替邻接矩阵的n行,并对图中的每个顶点v建立一个带头结点的单链表,将顶点v的相关信息存放在表头,表中的其余顶点用来存放与顶点v相关边的信息,例如其邻接点的编号、相应的边的权值。下图是相关图的邻接表形式。
在这里插入图片描述

在这里插入图片描述

2.图的邻接表存储结构声明

#define MAX_V 20
#define OK 1
#define ERROR 0
typedef int ElemType, Status;
typedef int GraphKind;    //定义图的类型,无向图0, 有向图1,无向网2,有向网3

//定义边的结点结构类型
typedef struct ArcNode {
	int adjvex;    //该边的终边编号
	int weight;    //该边的权值
	struct ArcNode* nextarc;
};
//定义顶点的结构类型
typedef struct VexNode{
	ElemType data;    //顶点的值
	ArcNode* firstarc;    //指向第一条与该顶点有关的指针
};

//定义邻接表表示的图
class ALGraph {
private:
	VexNode vexnode[MAX_V];    //定义邻接表
	int vexnum, arcnum;
	GraphKind type;    //定义图的类型,无向图0, 有向图1,无向网2,有向网3

public:
	Status CreateGraph();    //图的创建
	Status DestroyGraph();    //图的销毁
	ElemType GetVex(int v);    //返回编号v顶点的值
	Status InsertVex();    //插入顶点
	Status InsertArc(int v, int w);    //插入边
	Status DeleteVex(int v);    //删除顶点
	Status DeleteArc(int v, int w);    //删除边
	Status Print();    //打印邻接表
};

3.基于邻接表的无向图的创建

//基于邻接表的无向图创建
Status ALGraph::CreateGraph() {
	cin >> vexnum >> arcnum;    //输入图的顶点数和边数
	int i, v, w;
	for (i = 0; i < vexnum; i++) {    //输入各顶点的信息
		cin >> vexnode[i].data;
		vexnode[i].firstarc = NULL;
	}
	ArcNode* p;
	for (i = 0; i < arcnum; i++) {    //创建各边
		cin >> v >> w;    //输入一条边的两个顶点
		p = new ArcNode;    //创建一个用于存放当前边的顶点
		if (p == NULL) exit(0);
		p->adjvex = w;    //该边的起点是v终点是w
		p->nextarc = vexnode[v].firstarc;    //将该边结点链接到v号顶点
		vexnode[v].firstarc = p;

		p = new ArcNode;    //由于是无向图还要创建一条对称的边,起点w终点v
		if (p == NULL) exit(0);
		p->adjvex = v;
		p->nextarc = vexnode[w].firstarc;    //将该边链接到w号顶点
		vexnode[w].firstarc = p;
	}
	return OK;
}

4.基于邻接表的无向图插入顶点、插入边

//插入顶点
Status ALGraph::InsertVex() {
	int v, w;
	ArcNode* p;
	vexnum++;
	cin >> vexnode[vexnum - 1].data;
	vexnode[vexnum - 1].firstarc = NULL;
	while (true) {
		cin >> v >> w;
		if (v == -1)
			break;
		p = new ArcNode;
		if (p == NULL) exit(0);
		p->adjvex = w;    //该边的起点是v终点是w
		p->nextarc = vexnode[v].firstarc;    //将该边结点链接到v号顶点
		vexnode[v].firstarc = p;

		p = new ArcNode;    //由于是无向图还要创建一条对称的边,起点w终点v
		if (p == NULL) exit(0);
		p->adjvex = v;
		p->nextarc = vexnode[w].firstarc;    //将该边链接到w号顶点
		vexnode[w].firstarc = p;
		arcnum++;
	}
	return OK;
}

//插入边
Status ALGraph::InsertArc(int v, int w) {
	ArcNode* p;
	p = new ArcNode;
	if (p == NULL) exit(0);
	p->adjvex = w;    //该边的起点是v终点是w
	p->nextarc = vexnode[v].firstarc;    //将该边结点链接到v号顶点
	vexnode[v].firstarc = p;

	p = new ArcNode;    //由于是无向图还要创建一条对称的边,起点w终点v
	if (p == NULL) exit(0);
	p->adjvex = v;
	p->nextarc = vexnode[w].firstarc;    //将该边链接到w号顶点
	vexnode[w].firstarc = p;
	
	arcnum++;
	return OK;
}

5.基于邻接表的无向图删除顶点、删除边

//删除顶点
Status ALGraph::DeleteVex(int v) {
	ArcNode* p, * q;
	int i;
		p = q = vexnode[v].firstarc;
		while (p) {
			i = p->adjvex;
			ArcNode* p1, * q1;
			p1 = q1 = vexnode[i].firstarc;
			if (p1->adjvex == v) {
				vexnode[i].firstarc = p1->nextarc;
				delete p1;
				p1 = q1 = NULL;
			}
			else {
				while (true) {
					p1 = p1->nextarc;
					if (p1->adjvex == v) {
						q1->nextarc = p1->nextarc;
						delete p1;
						p1 = NULL;
						break;
					}
					q1 = p1;
				}
			}
			p = p->nextarc;
			delete q;
			q = p;
			arcnum--;
		}
		vexnode[v].firstarc = NULL;
		for (i = v; i < vexnum - 1; i++)
			vexnode[i] = vexnode[i + 1];
		vexnum--;
		return OK;
}

//删除边
Status ALGraph::DeleteArc(int v, int w) {
	ArcNode* p, *q;
	p = q = vexnode[v].firstarc;
	if (p->adjvex == w) {
		vexnode[v].firstarc = p->nextarc;
		delete p;
		p = q = NULL;
	}
	else {
		while (true) {
			p = p->nextarc;
			if (p->adjvex == w) {
				q->nextarc = p->nextarc;
				delete p;
				p = q = NULL;
				break;
			}
		}
	}

	p = q = vexnode[w].firstarc;
	if (p->adjvex == v) {
		vexnode[w].firstarc = p->nextarc;
		delete p;
		p = q = NULL;
	}
	else {
		while (true) {
			p = p->nextarc;
			if (p->adjvex == v) {
				q->nextarc = p->nextarc;
				delete p;
				p = q = NULL;
				break;
			}
		}
	}
	arcnum--;
	return OK;
}

6.邻接表的打印

//打印邻接表
Status ALGraph::Print() {
	int i;
	ArcNode* p;
	for (i = 0; i < vexnum; i++) {
		p = vexnode[i].firstarc;
		cout << vexnode[i].data << "-->";
		while (p) {
			cout << p->adjvex << " ";
			p = p->nextarc;
		}
		cout << endl;
	}
	return OK;
}

7.main函数

#include<iostream>
using namespace std;
#include"ALGraph.h"

int main() {
	ALGraph graph;
	graph.CreateGraph();
	graph.Print();
	cout << endl;
	cout << graph.GetVex(2)<< endl;
	graph.InsertVex();
	graph.Print();
	cout << endl;
	graph.InsertArc(0, 4);
	graph.Print();
	cout << endl;
	return OK;
}

运行结果

在这里插入图片描述

  • 16
    点赞
  • 105
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在C语言中,实现双向链表的基本操作包括链表的初始化、创建、查询、删除和释放。链表的查询和删除操作可以按照值或者按照结点的序号进行。 对于链表的初始化,可以创建一个空链表,将头指针和尾指针都指向NULL,表示链表为空。 链表的创建可以通过不断插入新的结点来实现。可以从头部或者尾部插入新的结点,并更新头指针和尾指针。 链表的查询操作可以按照值进行,遍历链表,找到目标值所在的结点。也可以按照结点的序号进行,从头结点开始顺序遍历到目标序号的结点。 链表的删除操作也可以按照值进行,遍历链表,找到目标值所在的结点并删除。也可以按照结点的序号进行,从头结点开始顺序遍历到目标序号的结点并删除。 最后,链表的释放操作可以遍历链表,逐个释放每个结点所占用的内存空间。 需要注意的是,双向链表相对于单向链表来说,每个结点都有指向前一个结点和后一个结点的指针,使得在插入和删除操作上更加灵活和方便。 另外,在实际应用中,链表的结构可以根据需求选择不同的组合,如单向带头、不带头循环、非循环等。其中,带头双向循环链表是最常用的链表结构,因为它的结构复杂,但是在实现时具有很多优势。而无头单向非循环链表则更多用于其他数据结构的子结构,如哈希桶、邻接表等。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [双向链表及其基本操作](https://download.csdn.net/download/qq_43355070/13074056)[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_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C语言详解双向链表的基本操作](https://blog.csdn.net/qq_61386381/article/details/124789295)[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_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值