c++实现图的操作(最小生成树和最短路径)

【题目描述】

  (1)图的深度优先搜索演示。

         要求:图采用邻接表存储结构,编程实现图的创建、图的深度优先搜索递归算法。        

    (2)图的广度优先搜索演示。

要求:图采用邻接表存储结构,编程实现图的创建、图的深度优先搜索递归算法。

(3)求带权无向图的最小生成树问题。

      (4)求带权有向图中从某个源点出发到其余各顶点的最短路径问题。

【代码实现】

废话不多说直接上代码

#include<iostream>
#include<queue>
using namespace std;
#define MVNum 100//最大的顶点数
#define Max 1000//没有权值时的∞
//边节点
typedef struct ArcNode {
	int adjvex;//该边所指向的顶点的位置
	ArcNode* nextarc;//指向下一条边的指针
	int info;//边的权值
};
//点节点
typedef struct VNode {
	char data;//顶点信息
	ArcNode* firstarc;//指向第一条依附该顶点的边的指针
};
//邻接表
typedef struct ALGragh {
	VNode vertices[MVNum];//顶点数组
	int visited[MVNum];//标志数组
	int vexnum, arcnum;//图当前顶点数和边数
};
//辅助数组的定义,用来记录从顶点集U到V-U的权值最小的边
typedef struct closeEdge {
	int adjvex;//最小边在U中的那个顶点
	int lowcost;//最小边的权值
};
//通过点的信息定位点的位置
int LocateVex(ALGragh G, char c) {
	for (int i = 0; i < G.vexnum; i++) {
		if (G.vertices[i].data == c) {
			return i;
		}
	}
}
//将标志数组归零,每次深度遍历图之后都要归零
void reZero(ALGragh &G) {
	for (int i = 0; i < G.vexnum; i++)	
		G.visited[i] = 0;
}
//邻接表创建带权无向图(undirected graph)
void CreateUDG(ALGragh& G) {
	cout << "请输入图的顶点数和边数:";
	cin >> G.vexnum >> G.arcnum;
	cout << "请依次输入各顶点信息(以空格分割):";
	for (int i = 0; i < G.vexnum; i++) {
		cin >> G.vertices[i].data;
		G.visited[i] = 0;//将标志数组初始化为0
		G.vertices[i].firstarc = NULL;
	}
	char v1, v2;
	int info;
	for (int k = 0; k < G.arcnum; k++) {
		cout << "请输入一条边的两个顶点信息和边的权值:";
		cin >> v1 >> v2 >> info;
		//确定v1和v2在G中的位置
		int i = LocateVex(G, v1); int j = LocateVex(G, v2);
		//分别将顶点v1和v2指向该条边
		ArcNode *p1 = new ArcNode;//生成一个新的边节点
		p1->adjvex = j; p1->info = info;
		p1->nextarc = G.vertices[i].firstarc;
		G.vertices[i].firstarc = p1;
		ArcNode* p2 = new ArcNode;
		p2->adjvex = i; p2->info = info;
		p2->nextarc = G.vertices[j].firstarc;
		G.vertices[j].firstarc = p2;
	}
}
//邻接表创建带权有向图(undirected graph)
void CreateDG(ALGragh& G) {
	cout << "请输入图的顶点数和边数:";
	cin >> G.vexnum >> G.arcnum;
	cout << "请依次输入各顶点信息(以空格分割):";
	for (int i = 0; i < G.vexnum; i++) {
		cin >> G.vertices[i].data;
		G.visited[i] = 0;//将标志数组初始化为0
		G.vertices[i].firstarc = NULL;
	}
	char v1, v2;
	int info;
	for (int k = 0; k < G.arcnum; k++) {
		cout << "请输入一条边的两个顶点信息和边的权值:";
		cin >> v1 >> v2 >> info;
		//确定v1和v2在G中的位置
		int i = LocateVex(G, v1); int j = LocateVex(G, v2);
		//有向图只需将v1连接该条边
		ArcNode* p1 = new ArcNode;//生成一个新的边节点
		p1->adjvex = j; p1->info = info;
		p1->nextarc = G.vertices[i].firstarc;
		G.vertices[i].firstarc = p1;
	}
}
//图的深度优先遍历
void DFS(ALGragh &G,char v) {
	cout << v; G.visited[LocateVex(G,v)] = 1;//将访问过的标志数组设为1
	ArcNode *p = G.vertices[LocateVex(G,v)].firstarc;//p指向v的边链表的第一个边节点
	while (p != NULL) {
		int w = p->adjvex;//w是v的邻接点
		//如果w未访问,则继续递归
		if (!G.visited[w])	DFS(G, G.vertices[w].data);
		p = p->nextarc;//否则继续深入
	}
}
//图的广度优先遍历
void BFS(ALGragh G, char v) {
	cout << v; G.visited[LocateVex(G, v)] = 1;
	queue<char> Q;
	Q.push(v);//v入队
	while (!Q.empty()) {
		char u = Q.front(); Q.pop();//队首出队
		ArcNode* p = G.vertices[LocateVex(G, u)].firstarc;
		while (p != NULL) {
			int w = p->adjvex;
			if (!G.visited[w]) {
				cout << G.vertices[w].data;//如果w未访问,则输出该节点
				G.visited[w] = 1;
				Q.push(G.vertices[w].data);//入队
			}
			p = p->nextarc;
		}
	}
}
//查找辅助数组的V-U中,边的最小值
int Min(closeEdge c[],int num) {
	int min = Max;//记录最小边的权值
	int flag = 0;//记录最小边所在顶点
	for (int i = 0; i<num; i++)
		if (c[i].lowcost < min && c[i].lowcost!=0) {
			min = c[i].lowcost;
			flag = i;
		}
	return flag;
}
//普利姆算法求从顶点u出发的最小生成树
void MiniSpanTree_Prim(ALGragh G, char u) {
	int k = LocateVex(G, u);//k为顶点u的下标
	closeEdge ce[MVNum];
	ArcNode* p = G.vertices[k].firstarc;
	//初始化辅助数组
	for (int j = 0;j<G.vexnum; j++) {
		if (j == k) ce[j].lowcost = 0;
		else ce[j] = { k,Max };
	}
	while (p!=NULL) {
		ce[p->adjvex].lowcost = p->info;
		p = p->nextarc;
	}
	//prim
	for (int i = 0; i < G.vexnum - 1; i++) {
		k = Min(ce,G.vexnum);
		int u0 = ce[k].adjvex;//最小边的顶点
		cout << G.vertices[u0].data << " " << G.vertices[k].data << " 权值:" << ce[k].lowcost << endl;
		ce[k].lowcost = 0;//将第k个顶点并入U集
		//新顶点并入U后重新选择最小边
		p = G.vertices[k].firstarc;
		while (p != NULL) {
			for (int j = 0; j < G.vexnum; j++)
				if (p->info < ce[j].lowcost && j == p->adjvex)
					ce[j] = { k,p->info };
			p = p->nextarc;
		}
	}
}
//用dijkstra算法求最短路径
void ShortestPath_Dijkstra(ALGragh G,char a) {
	bool S[MVNum];//记录从源点到终点是否已被确认是最短路径
	int D[MVNum];//记录从源点到终点的最短路径长度
	int Path[MVNum];//记录从源点到某一点是否有路径
	int v0 = LocateVex(G, a);
	ArcNode* p = G.vertices[v0].firstarc;
	//初始化
	for (int v = 0; v < G.vexnum; v++) {
		S[v] = false;
		D[v] = Max;
		Path[v] = -1;//无弧记为-1
	}
	while (p != NULL) {
		D[p->adjvex] = p->info;
		Path[p->adjvex] = v0;//v0到v有弧
		p = p->nextarc;
	}
	S[v0] = true; D[v0] = 0;//除去源点
	//初始化结束,开始主循环,每次求得v0到某个顶点的最短路径,将v加入S集
	for (int i = 1; i < G.vexnum; i++) {
		int min = Max;//min用来保存当前的最短路径,初始化为极大值
		int v;//用来记录当前终点
		for (int w = 0; w < G.vexnum; w++)
			if (!S[w] && D[w] < min) {
				v = w; min = D[w];
			}
		S[v] = true;
		//更新从v0出发到集合V-S上所有顶点的最短路径长度
		p = G.vertices[v].firstarc;
		while (p != NULL) {
			for (int w = 0; w < G.vexnum; w++)
				if (!S[w] && (D[v] + p->info < D[w]) && w == p->adjvex) {
					D[w] = D[v] + p->info;//更新D[w]
					Path[w] = v;//更改w的前驱为v
				}
			p = p->nextarc;
		}
	}
	//算法结束,输出从源点到各个点的最短路径
	for (int i = 0; i < G.vexnum; i++) {
		if (i!=v0) {
			cout << "从" << a << "到" << G.vertices[i].data << "的最短路径长度为:";
			cout << D[i] << endl;
		}
	}
}
int main() {
	ALGragh G1, G2;
	cout << "请创建带权无向图G1:" << endl;
	CreateUDG(G1);
	cout << "请创建带权有向图G2:" << endl;
	CreateDG(G2);
	cout << "深度优先遍历图" << endl;
	cout << "G1:"; DFS(G1, 'A');
	cout << "\nG2:"; DFS(G2, 'A');
	reZero(G1); reZero(G2);
	cout << "\n广度优先遍历图" << endl;
	cout << "G1:"; BFS(G1, 'A');
	cout << "\nG2:"; BFS(G2, 'A');
	cout << "\n普利姆算法求G1的最小生成树:" << endl;
	MiniSpanTree_Prim(G1,'A');
	cout << "迪杰斯特拉算法求G2的最短路径:" << endl;
	ShortestPath_Dijkstra(G2, 'A');
}

【功能测试】

1功能测试--图的创建

图 4.1 图的创建测试结果

图的创建就是先输入图的顶点数和边数,然后依次输出各顶点的信息(以空格分割),然后根据边的个数,依次输入每个边的两个顶点和边的权值,根据输入的顶点值根据LocateVex函数定位顶点数组的位置,之后创建边节点让两顶点指向该条边,然后接入顶点数组的指针上,有向图和无向图创建的区别只在生成几个边节点,无向图会生成两个边节点,而有向图只生成一个,因为它有方向。

2功能测试--深度优先遍历

图 4.2 深度优先遍历测试结果

深度优先遍历就是用栈来实现,先进后出,在这里我用的是递归来实现,在图的构造中我增加了一个标志数组(初始化值为0)用来标志访问过的顶点,选一个顶点,然后一直递归深入图,就是一条路走到黑,当顶点为空的时候再退回去继续递归,访问过的顶点,其对应的数组就设为1,直到全部的顶点全部访问过。

3功能测试--广度优先遍历

图 4.3 深度优先遍历测试结果

广度优先遍历就是用队列实现,先进先出,用c++标准库里的queue队列,将顶点保存到队列里,先将顶点的全部邻接顶点入队,然后依次出队顶点,重复循环该操作,这样就能实现广度优先遍历。

4功能测试--Prim算法求最小生成树

图 4.4 Prim算法求最小生成树测试结果

prim算法的核心就是归并顶点,使每一次循环找最小的边都是局部最小,然后一步步扩大它的范围。

在本题我学习课本增加了一个存放当前顶点与其它顶点的边的最小值closeEdge数组,然后在函数里对该数组初始化,将所有除它本身的顶点,都附上他们两点间的权值,若两点间没有权值则赋予最大值Max,且将初识顶点的lowcost设为0完成初始化。然后到程序的核心,首先在closeEdge数组中选择当前顶点的最小边作为并入最小生成树边的集合中,并将该边的顶点并入最小生成树的点中,同时将该点的lowcose置为0,说明该点已经连成,重复该步骤就能完成图的最小生成树。

本题的难点在于书上的例子用的是邻接矩阵来实现,我在创建图的过程用的是邻接表,所以我应找出邻接矩阵和邻接表的不同从而对该算法的修改。

5功能测试--Dijkstra算法求最短路径

图 4.5 Dijkstra算法求最短路径测试结果

Dijkstra算法的思想就是按路径长度递增的次序产生最短路径,每写一个顶点都是在上一个最短路径产生的,依次修改路径长度,从而达到最优。

在本题我从课本上学习,用了几个数组分别来储存路径的信息。S数组用于记录源点到某一个顶点是否已经是最短路径,D数组用于记录源点到某一个顶点的路径长度,Path数组用于记录某一个顶点和另一个顶点是否有弧,有弧则记录该顶点的下标,无弧则为-1,然后根据这些定义来初始化算法。

初始化结束,开始主循环,每次求得源点到某个顶点的最短路径,将找到的最短路径记录到S中。每次找到源点到某个顶点的最短路径,都要更新S数组为false的路径长度,重复循环完成算法。

本题的难点也是在于邻接矩阵和邻接表的不同从而需要对算法进行修改,比如在更新数组的时候,判断是否是该路径上的点需要加上该顶点是否等于p->adjvex。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值