数据结构实验三:图的操作与实现

一、实验目的:

1、图的邻接表和邻接矩阵存储
2、图的各种遍历算法实现
3、最小生成树的算法实现
4、最短路径的算法实现

二、使用仪器、器材

微机一台
操作系统:WinXP
编程软件:C++

三、实验内容及原理

利用图的邻接表或邻接矩阵存储结构设计并实现各种操作算法(任选一种存储结构来实现算法)。
1、图的邻接表和邻接矩阵存储
建立下图的邻接表或邻接矩阵,并输出之;
在这里插入图片描述
2、图的各种遍历算法实现
以0结点为起点实现上述图的深度优先和广度优先遍历算法;
3、最小生成树的算法实现
利用普里姆(Prim)或克鲁斯卡尔(Kruskal)算法求上图的最小生成树,算法实现代码必须有注释。
4、最短路径的算法实现
利用狄克斯特拉(Dijkstra)算法求上图中0结点到其它结点的最短路径,算法实现代码必须有注释。

四、实验过程原始数据记录

该程序使用全程使用邻接表的方式存储

/*
1、图的邻接表和邻接矩阵存储
建立下图的邻接表或邻接矩阵,并输出之;
    用 class 创建边结点arcnode ,顶点vnode ,邻接表 algraph
	在邻接表 algraph 包含创建无向图函数 CreateUDG(),输出邻接表函数 showUDG()

2、图的各种遍历算法实现
以0结点为起点实现上述图的深度优先和广度优先遍历算法;
	在邻接表 algraph 包含void DFS_AM( int v)深度优先搜索遍历函数,void BFS(int v)广度优先搜索遍历函数
	此时定义全局数组 visited 用来判断该顶点是否已遍历

3、最小生成树的算法实现
利用普里姆(Prim)或克鲁斯卡尔(Kruskal)算法求上图的最小生成树,算法实现代码必须有注释。
   在邻接表 algraph 包含void MiniSpanTree_Prim(int u) 用普里姆(Prim)算法创建最小树, 
   void showMini();按邻接表方式输出最小树

4、最短路径的算法实现
  利用狄克斯特拉(Dijkstra)算法求上图中0结点到其它结点的最短路径,算法实现代码必须有注释。
	在邻接表 algraph 包含void shortestpath_DIJ(int v0); //利用狄克斯特拉(Dijkstra)算法最短路径
*/

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

//**************************************************边结点
class ArcNode 
{
public:
	int adjvex;    //该边所指向结点的位置
	int cost;  //权值
	ArcNode *nextarc;  //指向下一条边的指针
};
//**************************************普里姆 最小生成树
class closedge  
{
public:
	int adjvex; //最小边所指向结点
	int lowcost;  //最小边权值
	closedge *nextarc;  //指向下一条边的指针
};
//*************************************************顶点信息
class VNode  
{
public:
	int data;              //顶点的值
	ArcNode *firstarc;  //指向第一条依附该节点的边的指针
	closedge *firstdge;  //指向第一条依附该节点的边的指针(最小树)
};
//************************************************邻接表
class ALGraph  
{
	VNode  *vertices = new VNode[50] ;  //顶点
	int vexnum, arcnum;  //顶点数和边数
	
public:
	void CreateUDG(); //用邻接表创建无向图
	void showUDG();  //输出邻接表
	void DFS_AM( int v); //从第v个顶点粗发进行深度优先搜索遍历
	void BFS(int v); //从第v个顶点粗发进行广度优先搜索遍历
	void MiniSpanTree_Prim(int u); //用普里姆(Prim)算法创建最小树
	void showMini(); //按邻接表方式输出最小树
	void shortestpath_DIJ(int v0); //利用狄克斯特拉(Dijkstra)算法最短路径
};

int main()
{
	ALGraph G;
	G.CreateUDG();
	G.showUDG();
	cout << endl << "深度优先遍历如下:";
	G.DFS_AM(0);
	cout << endl << "广度优先遍历如下:";
	G.BFS(0);
	G.MiniSpanTree_Prim(0);
	G.showMini();
	G.shortestpath_DIJ(0);
}

void ALGraph::CreateUDG()  //无向图
{

	/*******************自定义无向图,删掉下面建立题目图的代码,打开即可解锁

	cout << "请依次输入顶点数和边数: ";
	cin >> this->vexnum >> this->arcnum;
	cout << "请对应序号输入结点值:" << endl;
	for (int i = 0; i<this->vexnum; ++i) 
	{
		cout << "序号为" << i << "的结点值: ";
		cin >> this->vertices[i].data;
		this->vertices[i].firstarc = NULL;
	}
	*/
	//*****************建立题目中的无向图
	this->vexnum = 7; this->arcnum = 9;
	for (int i = 0; i < this->vexnum; ++i)
	{
		this->vertices[i].data = i;
		cout << "序号为" << i << "的点的值为:" << this->vertices[i].data << endl;
		this->vertices[i].firstarc = NULL;
	}

	cout << "请输入各条边依附的两个点的序号、该边的权值:" << endl;
	for (int i = 0; i < this->arcnum; ++i)
	{
		cout << "第" << i+1 << "条边连接的两个节点的序号: ";
		int v1, v2, c;
		cin >> v1 >> v2 >> c;

		ArcNode *p1 = new ArcNode;   //生成新的边结点
		p1->cost = c;
		p1->adjvex = v2;              //p1所指向v2的位置
		p1->nextarc = this->vertices[v1].firstarc; //p1下一条边的指针指向v1的第一条边
		this->vertices[v1].firstarc = p1;

		ArcNode *p2 = new ArcNode;   //生成新的边结点
		p2->cost = c;
		p2->adjvex = v1;  
		p2->nextarc = this->vertices[v2].firstarc;
		this->vertices[v2].firstarc = p2;
	}
}

void ALGraph::showUDG()
{
	cout << "序号    结点值 ---> 相连点的序号 --->...." << endl;
	for (int i = 0; i < this->vexnum; ++i)
	{
		if (vertices[i].firstarc != NULL) //若该节点没有相连的节点
		{
			cout << i << "       " << this->vertices[i].data << "      ";
			ArcNode *p1 = new ArcNode;
			p1 = vertices[i].firstarc;
			while (p1!= NULL)
			{
				cout << "--->" << p1->adjvex;
				p1 = p1->nextarc;
			}
			cout << endl;
		}
		else
		{
			cout << i << "       " << this->vertices[i].data << "      --->NULL" << endl;
		}
	}
}

bool *visited=new bool[50]; //全局数组,初值为true
void ALGraph::DFS_AM(int v)
{
	cout << v;
	visited[v] = false;
	ArcNode *p = new ArcNode;
	p = this->vertices[v].firstarc;
	while (p != NULL)
	{
		int w = p->adjvex;
		if (visited[w]) DFS_AM(w);
		p = p->nextarc;
	}
}

//利用深度优先时的数组visited,在深度优先后初值为false
void ALGraph::BFS(int v)
{
	cout << v;
	visited[v] = true;
	queue< int > q;
	q.push(v);  //v进队
	while (!q.empty())
	{
		int u = q.front();
		q.pop();
		ArcNode *p = new ArcNode;
		p = this->vertices[u].firstarc;
		while (p != NULL)
		{
			int w = p->adjvex;
			if (!visited[w])
			{
				cout << w;
				visited[w] = true;
				q.push(w);
			}
			p = p->nextarc;
		}
	}
}

//利用以上数组visited,两次遍历后初值为true
void ALGraph::MiniSpanTree_Prim(int u)
{
	for (int i = 0; i < this->vexnum; ++i) //初始化树的结点
	{
		this->vertices[i].firstdge = NULL;
	}
	visited[u] = false;
	for (int i = 0; i < this->vexnum - 1; i++) //生成的边数为 顶点数-1
	{
		int v1, v2;
		closedge *p1 = new closedge;   //最小树的边
		closedge *p2 = new closedge;   //最小树的边
		ArcNode *p = new ArcNode;
		p1->lowcost = 1000;
		for (int j = 0; j < this->vexnum; j++)
		{
			if (visited[j] == false)
			{
				p = this->vertices[j].firstarc;
				if (p->cost < p1->lowcost&&visited[p->adjvex] == true)
				{
					v2 = p->adjvex;
					v1 = j;
					p1->adjvex = v2;
					p1->lowcost = p->cost;
				}
				while (p != NULL)  //遍历目前结点的边
				{
					p = p->nextarc;
					if (p != NULL)
					{
						if (p->cost < p1->lowcost&&visited[p->adjvex] == true) //该边的权值小于最小边且该边所指结点未划入最小树
						{
							v2 = p->adjvex;
							v1 = j;
							p1->adjvex = v2;
							p1->lowcost = p->cost;
						}
					}
				}
			}
		}
		visited[p1->adjvex] = false;  //标记该节点以划入树中
		p1->nextarc = this->vertices[v1].firstdge; //p1下一条边的指针指向v1的第一条边
		this->vertices[v1].firstdge = p1;
	
		p2->lowcost = p1->lowcost;
		p2->adjvex = v1;
		p2->nextarc = this->vertices[v2].firstdge;
		this->vertices[v2].firstdge = p2;
	}
}

void ALGraph::showMini() 
{
	cout << endl << "最小树的邻接表:" << endl;
	cout << "序号    结点值 --权值--> 相连点的序号  --->...." << endl;
	for (int i = 0; i < this->vexnum; ++i)
	{
		cout << i << "       " << this->vertices[i].data << "      ";
		closedge *p1 = new closedge;
		p1 = vertices[i].firstdge;
		while (p1 != NULL)
		{
			cout << "  --"<<p1->lowcost<<"-->  " << p1->adjvex ;
			p1 = p1->nextarc;
		}
		cout << endl;
	}
}


void ALGraph::shortestpath_DIJ(int v0)
{
	int *path = new int[50]; //前驱数组
	int *D = new int[50]; //最短路径长度数组
	bool *S = new bool[50]; //记录是否求得v0到其他顶点最短路径 数组,初值为true
	ArcNode *p1 = new ArcNode;
	p1 = vertices[v0].firstarc;
	for (int i = 0; i < this->vexnum; ++i)   
	{
		S[i] = true;
		path[i] = -1;  //初始化前驱全为-1,即v0与v之间无弧
		D[i] = 1000; //无弧则路径无限大
	} 
	while (p1 != NULL)
	{
		path[p1->adjvex]=v0; //v0与v之间有弧,前驱为v0
		D[p1->adjvex] = p1->cost; //有弧则路径为权值
		p1 = p1->nextarc;
	}
	S[v0] = false;
	path[v0] = v0;
	D[v0] = 0;

	for (int i = 1; i < this->vexnum; ++i) //对其他vexnum-1个顶点求最短路径
	{
		int min = 1000;
		int v = 0;
		for (int w = 0; w < this->vexnum; ++w)                            
			if (S[w] == true && D[w] < min)  //寻找当前的最短路径
			{
				min = D[w];
				v = w;
			}
		S[v] = false;
		ArcNode *p2 = new ArcNode;
		int *Dvw = new int[50]; //计算v与其他顶点的最短路径
		p2 = vertices[v].firstarc;
		for (int i = 0; i < this->vexnum; ++i)
		{
			Dvw[i] = 1000; //无弧则路径无限大}
		}
		while (p2 != NULL)
		{
			Dvw[p2->adjvex] = p2->cost; //有弧则路径为权值
			p2 = p2->nextarc;
		}
		for (int w = 0; w < this->vexnum; ++w)
			if (S[w] == true && (D[v] + Dvw[w]) < D[w])  //若转弯比直走的权值小,则修改路径长度
			{
				D[w] = D[v] + Dvw[w];
				path[w] = v;
			}
	
	}

	cout << endl << "显示结点0到其他结点的最短路径:" << endl;
	for (int i = 0; i < this->vexnum; ++i)
	{
		if (path[i] != -1)
		{
			cout << vertices[i].data<<"--->";
			int p = path[i];
			while (p != v0)
			{
				cout << vertices[p].data<<"--->";
				p = path[p];
			}
			cout << vertices[v0].data << endl;
		}
		else cout << vertices[v0].data << "与" << vertices[i].data << "不连通" << endl;
	}
}

五、实验结果及分析

结点的值已在代码中按题目要求赋予,只需输入每条边连接的两个结点的序号,便可依次输出该图的邻接表、深度优先遍历结果、广度优先遍历结果、最小生成树的邻接表、结点0到其他结点的最短路径。该程序使用于连通无向图。
在这里插入图片描述

  • 8
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一个比较全面的论算法,下面是 C 语言的实现: ```c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAX_VERTEX_NUM 20 // 的最大顶点数 // 定义邻接矩阵结构体 typedef struct { int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 存储边的权值 int num_vertex; // 顶点数 } MGraph; // 定义邻接表结构体 typedef struct ArcNode { int adjvex; // 邻接点的下标 int weight; // 权值 struct ArcNode *next; // 指向下一个邻接点的指针 } ArcNode; typedef struct VNode { int data; // 顶点的数据 ArcNode *first; // 指向第一个邻接点的指针 } VNode, AdjList[MAX_VERTEX_NUM]; typedef struct { AdjList adjList; int num_vertex; // 顶点数 } ALGraph; // 初始化邻接矩阵 void initMGraph(MGraph *graph) { int i, j; printf("请输入顶点数:"); scanf("%d", &graph->num_vertex); // 输入边的权值 printf("请输入每条边的权值:\n"); for (i = 0; i < graph->num_vertex; i++) { for (j = 0; j < graph->num_vertex; j++) { scanf("%d", &graph->arcs[i][j]); } } } // 初始化邻接表 void initALGraph(ALGraph *graph) { int i, j, k, weight; ArcNode *p; printf("请输入顶点数:"); scanf("%d", &graph->num_vertex); // 输入顶点的数据 printf("请输入每个顶点的数据:\n"); for (i = 0; i < graph->num_vertex; i++) { graph->adjList[i].data = i; graph->adjList[i].first = NULL; } // 输入边的权值 printf("请输入每条边的起点、终点和权值:\n"); for (k = 0; k < graph->num_vertex; k++) { scanf("%d %d %d", &i, &j, &weight); p = (ArcNode*)malloc(sizeof(ArcNode)); p->adjvex = j; p->weight = weight; p->next = graph->adjList[i].first; graph->adjList[i].first = p; // 无向需加上下面这部分 p = (ArcNode*)malloc(sizeof(ArcNode)); p->adjvex = i; p->weight = weight; p->next = graph->adjList[j].first; graph->adjList[j].first = p; } } // 广度优先遍历 void BFS(MGraph graph, int start) { int i, j; bool visited[MAX_VERTEX_NUM] = {false}; // 记录每个顶点是否被访问过 int queue[MAX_VERTEX_NUM], front = 0, rear = 0; // 定义队列,front 表示队头,rear 表示队尾 printf("广度优先遍历结果:"); printf("%d ", start); visited[start] = true; queue[rear++] = start; // 将起点入队 while (front != rear) { // 队列不为空 i = queue[front++]; // 出队 for (j = 0; j < graph.num_vertex; j++) { if (graph.arcs[i][j] != 0 && visited[j] == false) { // 如果 i 和 j 有边,并且 j 没有被访问过 printf("%d ", j); visited[j] = true; queue[rear++] = j; // 将 j 入队 } } } printf("\n"); } void BFS_AL(ALGraph graph, int start) { int i; bool visited[MAX_VERTEX_NUM] = {false}; // 记录每个顶点是否被访问过 int queue[MAX_VERTEX_NUM], front = 0, rear = 0; // 定义队列,front 表示队头,rear 表示队尾 ArcNode *p; printf("广度优先遍历结果:"); printf("%d ", start); visited[start] = true; queue[rear++] = start; // 将起点入队 while (front != rear) { // 队列不为空 i = queue[front++]; // 出队 p = graph.adjList[i].first; while (p != NULL) { if (visited[p->adjvex] == false) { // 如果 j 没有被访问过 printf("%d ", p->adjvex); visited[p->adjvex] = true; queue[rear++] = p->adjvex; // 将 j 入队 } p = p->next; } } printf("\n"); } // 深度优先遍历 void DFS(MGraph graph, int start, bool visited[]) { int i; visited[start] = true; printf("%d ", start); for (i = 0; i < graph.num_vertex; i++) { if (graph.arcs[start][i] != 0 && visited[i] == false) { // 如果 start 和 i 有边,并且 i 没有被访问过 DFS(graph, i, visited); } } } void DFS_AL(ALGraph graph, int start, bool visited[]) { ArcNode *p; visited[start] = true; printf("%d ", start); p = graph.adjList[start].first; while (p != NULL) { if (visited[p->adjvex] == false) { // 如果 j 没有被访问过 DFS_AL(graph, p->adjvex, visited); } p = p->next; } } // 深度优先遍历的入口函数 void DFSTraverse(MGraph graph) { int i; bool visited[MAX_VERTEX_NUM] = {false}; printf("深度优先遍历结果:"); for (i = 0; i < graph.num_vertex; i++) { if (visited[i] == false) { DFS(graph, i, visited); } } printf("\n"); } void DFSTraverse_AL(ALGraph graph) { int i; bool visited[MAX_VERTEX_NUM] = {false}; printf("深度优先遍历结果:"); for (i = 0; i < graph.num_vertex; i++) { if (visited[i] == false) { DFS_AL(graph, i, visited); } } printf("\n"); } // Prim 算法 void Prim(MGraph graph) { int i, j, k, min, sum = 0; int lowcost[MAX_VERTEX_NUM], closest[MAX_VERTEX_NUM]; for (i = 1; i < graph.num_vertex; i++) { lowcost[i] = graph.arcs[0][i]; closest[i] = 0; } for (i = 1; i < graph.num_vertex; i++) { min = 0x7fffffff; for (j = 1; j < graph.num_vertex; j++) { if (lowcost[j] != 0 && lowcost[j] < min) { min = lowcost[j]; k = j; } } printf("%d %d %d\n", closest[k], k, min); sum += min; lowcost[k] = 0; for (j = 1; j < graph.num_vertex; j++) { if (graph.arcs[k][j] != 0 && graph.arcs[k][j] < lowcost[j]) { lowcost[j] = graph.arcs[k][j]; closest[j] = k; } } } printf("最小生成树的权值之和为:%d\n", sum); } // Kruskal 算法 typedef struct { int u, v; // 两个端点的下标 int weight; // 权值 } Edge; int cmp(const void *a, const void *b) { // 比较函数 return ((Edge*)a)->weight - ((Edge*)b)->weight; } int Find(int parent[], int f) { // 找父节点 if (parent[f] == f) return f; else return parent[f] = Find(parent, parent[f]); } void Kruskal(MGraph graph) { int i, j, k, sum = 0; int parent[MAX_VERTEX_NUM]; Edge edges[MAX_VERTEX_NUM]; for (i = 0, k = 0; i < graph.num_vertex; i++) { for (j = i + 1; j < graph.num_vertex; j++) { if (graph.arcs[i][j] != 0) { edges[k].u = i; edges[k].v = j; edges[k].weight = graph.arcs[i][j]; k++; } } } qsort(edges, k, sizeof(edges[0]), cmp); // 将边按权值从小到大排序 for (i = 0; i < graph.num_vertex; i++) { parent[i] = i; // 初始化每个点的父节点为其本身 } printf("最小生成树的边为:\n"); for (i = 0; i < k; i++) { int faU = Find(parent, edges[i].u); int faV = Find(parent, edges[i].v); if (faU != faV) { // 如果 u 和 v 不在同一个连通块中 printf("%d %d %d\n", edges[i].u, edges[i].v, edges[i].weight); sum += edges[i].weight; parent[faU] = faV; // 将 u 的父节点设为 v } } printf("最小生成树的权值之和为:%d\n", sum); } int main() { MGraph graph_M; ALGraph graph_AL; int choice; printf("请选择存储方式(1.邻接矩阵,2.邻接表):"); scanf("%d", &choice); if (choice == 1) { initMGraph(&graph_M); BFS(graph_M, 0); DFSTraverse(graph_M); Prim(graph_M); Kruskal(graph_M); } else { initALGraph(&graph_AL); BFS_AL(graph_AL, 0); DFSTraverse_AL(graph_AL); // Prim 和 Kruskal 算法同上 } return 0; } ``` 注意:这只是一个简单的实现,如果需要在实际项目中使用,还需要考虑更多的情况,比如错误处理、异常情况处理等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值