图的邻接表实现

目录

为什么使用邻接表

邻接表的结构

 邻接表创建及增删改的操作


为什么使用邻接表

当图中的边数相对于顶点较少时,邻接矩阵是对存储空间的极大浪费

我们可以考虑对边或弧使用链式存储的方式来避免空间浪费的问题

我们把这种数组与链表相结合的存储方法称为邻接表

邻接表的结构

图的结构体

1.顶点数、弧数

2.图的类型

3.邻接表 

typedef int ArcType;

enum GraphKind {
	DG,
	DN,
	UDG,
	UDN,
};

struct Vertex {
	char name[MAX_NAME_SIZE];
};

struct Graph {
	int verNum, arcNum;
	GraphKind kind;
	AdjList adj;
};

邻接表AdjList[MAX_VERTEX_NUM]

 1.顶点

2.弧指针firstArc

typedef struct VertexNode {
	Vertex vertex;
	ArcNode* firstArc;
}AdjList[MAX_VERTEX_NUM];

 记录弧的结点

 1.data 和指针(链式结构)

2.data 里存储的是以该结点为弧尾的弧的弧头,弧的权值

struct ElemType {
	int headLoc;
	ArcType value;
};

struct ArcNode {
	ElemType data;
	ArcNode* next;
};

 

 邻接表创建及增删改的操作

 创建图

1.输入类型、弧数、点数

2.输入vernum个顶点的name ,并使每一个顶点的firstArc=NULL

3.输入弧尾、弧头,若是网,输入权值

4.使用头插法构建邻接表(若是无向图,把弧尾和弧头反过来再创建一个结点即可)

     *输入名字获得顶点下标的函数*

int VertexLoc(Graph G, Vertex v) {
	for (int i = 0; i < G.verNum; i++)
		if (strcmp(G.adj[i].vertex.name, v.name) == 0)
			return i;
	return -1;
}

头插法理解 

 

void CreateGraph(Graph& G) {
	cout << "请输入图的类型(0:有向图, 1:有向网, 2:无向图, 3:无向网)" << endl;
	int kind;
	cin >> kind;
	G.kind = (GraphKind)kind;

	cout << "请输入顶点数、弧数" << endl;
	cin >> G.verNum >> G.arcNum;

	for (int i = 0; i < G.verNum; i++) {
		cout << "请输入第" << i + 1 << "个顶点的名称" << endl;
		cin >> G.adj[i].vertex.name;
		G.adj[i].firstArc = NULL;
	}

	Vertex head, tail;
	int headLoc, tailLoc;
	int value = 1;

	for (int i = 0; i < G.arcNum; i++) {
		cout << "请输入第" << i + 1 << "条弧的 弧尾、弧头: ";
		cin >> tail.name >> head.name;
		if (G.kind % 2 == 1) {
			//网
			cout << "请输入权值: ";
			cin >> value;
		}
		headLoc = VertexLoc(G, head);
		tailLoc = VertexLoc(G, tail);
		ArcNode* p = new ArcNode;
		p->data.headLoc = headLoc;
		p->data.value = value;
		//头插法,O(1),最先插入的顶点的next = NULL
		p->next = G.adj[tailLoc].firstArc;
		G.adj[tailLoc].firstArc = p;
		if (G.kind > 1) {
			//无向
			ArcNode* q = new ArcNode;
			q->data.headLoc = tailLoc;
			q->data.value = value;
			q->next = G.adj[headLoc].firstArc;
			G.adj[headLoc].firstArc = q;
		}
	}
}

 打印图

循环cout: 从第0个顶点到第verNum个,从每个的顶点的firstArc到NULL 

void Display(Graph G) {
	ArcNode* p;
	for (int i = 0; i < G.verNum; i++) {
		p = G.adj[i].firstArc;
		cout << G.adj[i].vertex.name << "为弧尾的弧的弧头有:" << endl;
		while (p) {

			cout << G.adj[i].vertex.name << "->" << G.adj[p->data.headLoc].vertex.name << " : " << p->data.value << endl;
			p = p->next;
		}
		cout << endl;
	}
	cout << endl;
}

 Some Function (≧∇≦)ノ

//获取序号为v的顶点
Vertex GetVex(Graph G, int v) {
	if (v < 0 || v >= G.verNum)
		exit(ERROR);
	return G.adj[v].vertex;
}

//对顶点v赋新值newV
status PutVex(Graph G, Vertex v, Vertex newV) {
	int vLoc = VertexLoc(G, v);
	if (vLoc == -1)
		return ERROR;
	G.adj[vLoc].vertex = newV;
	return OK;
}

//返回序号为v的顶点的第一个邻接点的序号
int FirstAdjvex(Graph G, int v) {
	if (v < 0 || v >= G.verNum)
		return -1;
	ArcNode* p = G.adj[v].firstArc;
	if (p)
		return p->data.headLoc;
	else
		return -1;
}

//判断两个弧头是否相等
status EqualVertex(ElemType e1, ElemType e2) {
	if (e1.headLoc == e2.headLoc)
		return OK;
	else
		return ERROR;
}
//返回v的(相对于w的)下一个邻接点的序号,若w是v的最后一个顶点,则返回-1
//v:序号v;  w:序号v的顶点的邻接点的序号
int NextVertex(Graph G, int v, int w) {
	if (v < 0 || v >= G.verNum)
		return -1;
	ArcNode* p = G.adj[v].firstArc;
	while (p && p->data.headLoc != w)
		p = p->next;
	if (!p || !p->next)
		return -1;
	else
		return p->next->data.headLoc;
}

 顶点和弧的插入删除

 插入顶点——数组加一个元素(记得初始化和verNum++)

插入弧——(和创建图中一样,头插法)

删除弧——注意特殊情况

删除顶点——删除和v关联的弧,点数组和弧链表中受到影响的下标都要调整

//插入顶点
void InsertVertex(Graph& G, Vertex v) {
	if (G.verNum == MAX_VERTEX_NUM)
		exit(OVERFLOW);
	G.adj[G.verNum].vertex = v;
	G.adj[G.verNum].firstArc = NULL;
	G.verNum++;
}

//插入弧
status InsertArc(Graph& G, Vertex tail, Vertex head) {
	int tailLoc, headLoc;
	tailLoc = VertexLoc(G, tail);
	headLoc = VertexLoc(G, head);
	if (tailLoc == -1 || headLoc == -1)
		return ERROR;
	int value = 1;
	if (G.kind % 2 == 1) {
		//网
		cout << "请输入权值: ";
		cin >> value;
	}
	ArcNode* p = new ArcNode;
	p->data.headLoc = headLoc;
	p->data.value = value;
	p->next = G.adj[tailLoc].firstArc;
	G.adj[tailLoc].firstArc = p;

	if (G.kind > 1) {
		//无向
		ArcNode* q = new ArcNode;
		q->data.headLoc = tailLoc;
		q->data.value = value;
		q->next = G.adj[headLoc].firstArc;
		G.adj[headLoc].firstArc = q;
	}
	G.arcNum++;
	return OK;
}

//删除弧
status DeleteArc(Graph& G, Vertex tail, Vertex head) {
	int tailLoc = VertexLoc(G, tail);
	int headLoc = VertexLoc(G, head);
	if (tailLoc == -1 || headLoc == -1 || tailLoc == headLoc)
		return ERROR;
	ArcNode* p, * q;
	p = G.adj[tailLoc].firstArc;
	//如果第一个顶点就是弧头
	if (p && p->data.headLoc == headLoc) {
		q = p->next;
		G.adj[tailLoc].firstArc = q;
		free(p);
	}
	else {
		//只有一个顶点
		if (p && p->next == NULL)
			return ERROR;
		while (p && p->next->data.headLoc != headLoc)
			p = p->next;
		if (!p)
			return ERROR;
		q = p->next;
		p->next = q->next;
		free(q);
	}

	if (G.kind > 1) {
		//无向
		ArcNode* p2, * q2;
		p2 = G.adj[headLoc].firstArc;
		//如果第一个就是
		if (p2 && p2->data.headLoc == tailLoc) {
			q2 = p2->next;
			G.adj[headLoc].firstArc = q2;
			free(p2);
		}
		else {
			//只有一个顶点
			if (p2 && p2->next == NULL)
				return ERROR;
			while (p2 && p2->next->data.headLoc != tailLoc)
				p2 = p2->next;
			if (!p2)
				return ERROR;
			q2 = p2->next;
			p2->next = q2->next;
			free(q2);
			q2 = NULL;
		}
	}
	G.arcNum--;
	return OK;
}

//删除点
status DeleteVertex(Graph& G, Vertex v) {
	int vLoc = VertexLoc(G, v);
	if (vLoc == -1)
		return ERROR;
	for (int i = 0; i < G.verNum; i++)
		DeleteArc(G, v, G.adj[i].vertex);		//删除由v发出的弧


	//如果有向,手动删除发向v的弧
	if (G.kind < 2)
		for (int i = 0; i < G.verNum; i++)
			DeleteArc(G, G.adj[i].vertex, v);

	//调整下标
	for (int i = 0; i < G.verNum; i++) {
		ArcNode* p = G.adj[i].firstArc;
		while (p) {
			if (p->data.headLoc > vLoc)
				p->data.headLoc--;
			p = p->next;
		}
	}

	for (int i = vLoc + 1; i < G.verNum; i++)
		G.adj[i - 1] = G.adj[i];
	G.verNum--;
	return OK;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值