哈工大数据结构实验3-图形结构及其应用

本文仅供参考,严禁抄袭!

实验目的

通过实现Dijkstra算法和Floyd-Warshall算法,理解各种情况下最短路径求解的基本思想,并通过堆优化理解算法的优化思想,熟练运用各种数据结构实现对图的存储。

实验要求及实验环境

实验要求

  1. 实现单源最短路径的 Dijkstra 算法,输出源点及其到其他顶点的最短路径长
    度和最短路径
  2. 利用堆结构(实现的优先级队列),改进和优化 Dijkstra 算法的实现;
  3. 实现全局最短路径的 Floyd-Warshall 算法。计算任意两个顶点间的最短距离
    矩阵和最短路径矩阵,并输出任意两个顶点间的最短路径长度和最短路径。
  4. 利用 Dijkstra 或 Floyd-Warshall 算法解决单目标最短路径问题:找出图中每
    个顶点 v 到某个指定顶点 c 最短路径;
  5. 利用 Dijkstra 或 Floyd-Warshall 算法解决单顶点对间最短路径问题:对于某
    对顶点 u 和 v,找出 u 到 v 和 v 到 u 的一条最短路径 ;
  6. (选做)利用 Floyd-Warshall 算法,计算有向图的可达矩阵,理解可达矩阵
    的含义;
  7. 以文件形式输入图的顶点和边,并显示相应的结果。要求顶点不少于 10 个,
    边不少于 13 个;
  8. 软件功能结构安排合理,界面友好,便于使用.

实验环境

Visual Studio 2019, Windows 10

设计思想(本程序中的用到的所有数据类型的定义,主程序的流程图及各程序模块之间的调用关系)

堆的数据结构

  1. 逻辑设计:二叉树
  2. 物理设计:结构体数组,数组的每个节点由weight(权值)和key(在原数组中的下标)组成。
  3. 操作:
    Initial():初始化堆;
    DeleteMin():删除堆中最小元素;
    MakeNullHeap():将堆置空;
    HeapEmpty():堆是否为空;
    HeapFull():堆是否满;
    Insert():将某一元素插入堆;

图的存储结构1

  1. 逻辑设计:邻接矩阵
  2. 物理设计:结构体MTGraph:其中包含存储顶点表的数组vertex,存储矩阵的二维数组,存储边和顶点数量的整型数n,e。

图的存储结构2

  1. 逻辑设计:邻接表
  2. 物理设计:结构体AdjGraph:结构体VertexNode存储的顶点表:verlist,其中VertexNode存储顶点信息和指向其出边的指针firstedge,边表节点由结构体EdgeNode组成,包括指向顶点的下标adjvex,边的权重cost,指向下一条边的指针next

流程图

在这里插入图片描述

调用关系

  1. 删除堆最小值DeleteMin()调用了HeapEmpty()函数;
  2. 堆优化Dijkstra函数DijkstrabyHeapPro()调用了MakeNullHeap(将堆置空),MatrixToLink(邻接矩阵转邻接表),Initial(初始化堆),DeleteMin(删除堆中最小值),Insert(将某一元素插入堆)五个函数;
  3. 求解单目标最短路径的Trans函数调用了DijkstraPro(Dijkstra算法的修改,使其可求单目标最短路径);
  4. 求两个顶点间最短路径的TwoVertex函数调用了FloydPro函数(Floyd算法的调整,使其只输出给定两顶点间的最短路径);

测试结果

用例:
输入(第一行两个数分别为顶点数和边数,第二行为两边的顶点及边之权重):
10 16
1 3 34 1 6 100 2 7 72 2 5 89 3 4 90 3 6 21 3 5 55 4 3 67 5 3 92 6 7 45 7 2 86 8 1 33 8 9 9 9 5 10 10 9 17 10 4 28
在这里插入图片描述
输出:
在这里插入图片描述

源代码

#include<iostream>
#include<vector>
#include<string>
#include<fstream>
#include<algorithm>
#include<stack>
#define Maxlength 200
#define Infinity 10000
#define HeapMaxSize 200
using namespace std;
typedef struct {
	vector<int>vertex;
	int Matrix[Maxlength][Maxlength];
	int n, e;
}MTGraph;
typedef struct {
	int weight, key;
}Elementype;//用于堆的一个数据类型
typedef struct {
	Elementype elements[HeapMaxSize];
	int n;
}HEAP;//堆的定义
typedef struct node {
	int adjvex;
	int cost;
	struct node* next;
}EdgeNode;
typedef struct {
	int vertex;
	EdgeNode* firstedge;
}VertexNode;
typedef struct {
	VertexNode verlist[Maxlength];
	int n, e;
}AdjGraph;
void MakeNullHeap(HEAP& heap)//将堆置空
{
	heap.n = 0;
}
bool HeapFull(HEAP heap)
{
	return (heap.n == HeapMaxSize - 1);
}//判断堆是否为满
bool HeapEmpty(HEAP heap)//判断堆是否为空
{
	return (!heap.n);
}
void Initial(HEAP& heap, int num, vector<int>V, int D[])//将堆初始化
{
	int j;
	for (int i = 0; i < num; i++)
	{
		j = heap.n + 1;
		while ((j != 1) && (D[V[i]] < heap.elements[j / 2].weight))
		{
			heap.elements[j] = heap.elements[j / 2];
			j /= 2;
		}
		heap.elements[j].weight = D[V[i]];
		heap.elements[j].key = V[i];
		heap.n++;
	}
}
void DeleteMin(HEAP& heap)//删除最小元素
{
	Elementype temp;
	int parent = 1, child = 2;
	if (!HeapEmpty(heap))
	{
		temp = heap.elements[heap.n--];
		while (child <= heap.n)
		{
			if ((child < heap.n) && (heap.elements[child].weight > heap.elements[child + 1].weight))
			{
				child++;
			}
			if (temp.weight <= heap.elements[child].weight)
				break;
			heap.elements[parent] = heap.elements[child];
			parent = child;
			child *= 2;
		}
		heap.elements[parent] = temp;
	}
}
void MatrixToLink(MTGraph matrix, AdjGraph& link)//邻接矩阵转邻接表
{
	for (int i = 0; i < matrix.n; i++)
	{
		link.verlist[i].vertex = matrix.vertex[i];
		link.verlist[i].firstedge = NULL;
		link.n++;
		for (int j = 0; j < matrix.n; j++)
		{
			if (matrix.Matrix[i][j] != 0)
			{
				EdgeNode* p = new EdgeNode, * q = link.verlist[i].firstedge;
				if (q == NULL)
				{
					p->adjvex = j;
					p->cost = matrix.Matrix[i][j];
					p->next = NULL;
					link.verlist[i].firstedge = p;
				}
				else
				{
					while (q->next != NULL)
						q = q->next;
					p->adjvex = j;
					p->cost = matrix.Matrix[i][j];
					p->next = NULL;
					q->next = p;
				}
				link.e++;
			}
		}
	}
}
void Insert(HEAP& heap, int i, int weight)//更新堆中最小节点
{
	int j;
	if (!HeapFull(heap))
	{
		j = heap.n + 1;
		while ((j != 1) && (weight < heap.elements[j / 2].weight))
		{
			heap.elements[j] = heap.elements[j / 2];
			j /= 2;
		}
		heap.elements[j].weight = weight;
		heap.elements[j].key = i;
		heap.n++;
	}
}
void DijkstrabyHeapPro(MTGraph graph)//堆优化后的Dijkstra算法,时间复杂度为O(n*log e)
{
	int min, temp, start;
	int D[Maxlength];//存储最短路径
	int path[Maxlength] = { 0 };
	int visit[Maxlength] = { 0 };
	HEAP heap;
	MakeNullHeap(heap);
	AdjGraph link;
	MatrixToLink(graph, link);
	vector<int>S; //存储已选顶点
	vector<int>V;//存储未选定点
	V = graph.vertex;
	vector<int>::iterator k = V.begin();
	cout << "请输入源点:";
	cin >> start;
	start--;
	for (k = V.begin(); k != V.end();)
	{
		if (*k == start)
		{
			V.erase(k);
			break;
		}
		else
			++k;
	}
	for (int i = 0; i < graph.n; i++)
		D[i] = graph.Matrix[start][i];
	Initial(heap, V.size(), V, D);
	for (int i = 1; i < graph.n; i++)
	{
		min = heap.elements[1].key;
		
		while (visit[min] == 1)
		{
			DeleteMin(heap);
			min = heap.elements[1].key;
		}
		if (visit[min] == 0)
		{
			DeleteMin(heap);
			visit[min] = 1;
		}
		S.push_back(min);
		for (k = V.begin(); k != V.end();)
		{
			if (*k == min)
			{
				V.erase(k);
				break;
			}
			else
				++k;
		}
		EdgeNode* p = link.verlist[min].firstedge;
		while (p)
		{
			if (D[p->adjvex] > (D[min] + graph.Matrix[min][p->adjvex]))
			{
				D[p->adjvex] = D[min] + graph.Matrix[min][p->adjvex];
				temp = p->adjvex;
				path[temp] = min;
				Insert(heap, p->adjvex, D[p->adjvex]);
			}
			p = p->next;
		}
	}

	sort(S.begin(), S.end());
	cout << "单源最短路径:";//打印最短路径
	for (auto x : S)
		if (D[x] != Infinity)
		{
			cout << start + 1 << "->" << x + 1 << ":" << D[x] << ", ";
		}
	cout << endl;
	stack<int>SS;
	int t;
	for (int i = 0; i < graph.n; i++)
	{
		if (i != start)
		{
			cout << start + 1 << "到" << i + 1 << "的最短路径为:" << start + 1;
			t = path[i];
			while (t != 0)
			{
				SS.push(t);
				t = path[t];
			}
			while (!SS.empty())
			{
				cout << "->" << SS.top() + 1;
				SS.pop();
			}
			cout << "->" << i + 1 << endl;
		}
	}
}
//从文件中读入数据到邻接矩阵
void ReadInMatirx(MTGraph& graph)
{
	ifstream in("map.txt");
	int n,e;//读入的第一行,存储的是顶点信息;第二行,存储的是边信息
	int a, b, c;
	int i = 0, j = 0;
	in >> n >> e;
	graph.e = e; graph.n = n;
	for (i=0;i<graph.n;i++)
	{
		graph.vertex.push_back(i);
	}
	for (int m = 0; m < graph.n; m++)
		for (int l = 0; l < graph.n; l++)
			graph.Matrix[m][l] = Infinity;
	for (int m = 0; m < graph.n; m++)
		graph.Matrix[m][m] = 0;

	for(j=0;j<graph.e;j++)
	{
		in >> a >> b >> c;
		graph.Matrix[a-1][b-1] = c;
	}
	in.close();
}
void Floyd(MTGraph graph)//Floyd算法求每个顶点间的最短路径
{
	int A[Maxlength][Maxlength], path[Maxlength][Maxlength];
	int i, j, k, vertex;
	for (i = 0; i < graph.n; i++)
		for (j = 0; j < graph.n; j++)
		{
			A[i][j] = graph.Matrix[i][j];
			path[i][j] = j;
		}
	for (k = 0; k < graph.n; k++)
	{
		for (i = 0; i < graph.n; i++)
		{
			for (j = 0; j < graph.n; j++)
			{
				if (A[i][k] + A[k][j] < A[i][j] && A[i][k]!=0 &&A[k][j]!=0)
				{
					A[i][j] = A[i][k] + A[k][j];
					path[i][j] = path[i][k];//记录路径
				}
			}
		}
	}
	for (i = 0; i < graph.n; i++)
	{
		for (j = 0; j < graph.n; j++)
			printf("%6d", A[i][j]);
		cout << endl;
	}
	for (i = 0; i < graph.n; i++)//输出每个顶点间最短路径
	{
		for (j = 0; j < graph.n; j++)
		{
			if (A[i][j] != 0 && A[i][j] != Infinity)
			{
				cout << i + 1 << "到" << j + 1 << "的最短路径为:" << i + 1;
				vertex = path[i][j];
				while (vertex != j)
				{
					cout << "->" << vertex + 1;
					vertex = path[vertex][j];
				}
				cout << "->" << vertex + 1 << endl;
			}
		}
	}

}
void Warshall(MTGraph graph)//Warshall算法求可达矩阵
{
	int A[Maxlength][Maxlength];
	int i, j, k;
	for (i = 0; i < graph.n; i++)
		for (j = 0; j < graph.n; j++)
		{
			if (graph.Matrix[i][j] == Infinity)
				A[i][j] = 0;
			else
				A[i][j] = graph.Matrix[i][j];
		}
	for (k = 0; k < graph.n; k++)
	{
		for (i = 0; i < graph.n; i++)
		{
			for (j = 0; j < graph.n; j++)
			{
				A[i][j] = A[i][j] || (A[i][k] && A[k][j]);
			}
		}
	}
	for (i = 0; i < graph.n; i++)
	{
		for (j = 0; j < graph.n; j++)
			printf("%3d", A[i][j]);
		cout << endl;
	}
}
void DijkstraPro(MTGraph graph, int start)//Dijkstra算法的改动,使其可求单目标点的最短路径,大致思路是把邻接矩阵转置后应用Dijkstra
{
	int min, temp;
	int D[Maxlength];//存储最短路径
	int path[Maxlength] = { 0 };
	vector<int>S; //存储已选顶点
	vector<int>V;//存储未选定点
	V = graph.vertex;
	vector<int>::iterator k = V.begin();
	for (k = V.begin(); k != V.end();)
	{
		if (*k == start)
		{
			V.erase(k);
			break;
		}
		else
			++k;
	}
	for (int i = 0; i < graph.n; i++)
		D[i] = graph.Matrix[start][i];
	for (int i = 1; i < graph.n; i++)
	{
		min = V[0];
		for (auto x : V)
		{
			if (D[x] < D[min])
			{
				min = x;
			}
		}
		S.push_back(min);
		for (k = V.begin(); k != V.end();)
		{
			if (*k == min)
			{
				V.erase(k);
				break;
			}
			else
				++k;
		}
		for (auto x : V)
		{
			if (D[x] > (D[min] + graph.Matrix[min][x]) && graph.Matrix[min][x]!=0)
			{
				D[x] = D[min] + graph.Matrix[min][x];
				temp = x;
				path[temp] = min;
			}
		}
	}
	sort(S.begin(), S.end());
	int flag = 0;
	cout << "单源最短路径:";
	for (auto x : S)
	{
		if (D[x] != Infinity)
		{
			cout << x + 1 << "->" << start + 1 << ":" << D[x] << ", ";
			flag = 1;
		}
		
	}
	cout << endl;
	int t;
	if (flag)
	{
		for (auto i:S)
		{
			if (i != start && D[i]!= Infinity)
			{
				cout << i + 1 << "到" << start + 1 << "的最短路径为:" << i + 1;
				t = path[i];
				while (t != 0)
				{
					cout << "->" << t + 1;
					t = path[t];
				}
				cout << "->" << start + 1 << endl;
			}
		}
	}
	else
		cout << "无" << endl;
}
void Trans(MTGraph graph)//调用Dijkstra算法求单目标最短路径的函数
{
	int dest;
	MTGraph matrix=graph;
	for (int i = 0; i < graph.n; i++)
	{
		for (int j = 0; j < graph.n; j++)
		{
			matrix.Matrix[j][i] = graph.Matrix[i][j];
		}
	}
	cout << "请输入目标点:";
	cin >> dest;
	DijkstraPro(matrix, dest-1);
}
void FloydPro(MTGraph graph, int verx1,int verx2)//Floyd的改动,即只输出给定两顶点间的最短路径
{
	int A[Maxlength][Maxlength], path[Maxlength][Maxlength];
	int i, j, k, vertex;
	for (i = 0; i < graph.n; i++)
		for (j = 0; j < graph.n; j++)
		{
			A[i][j] = graph.Matrix[i][j];
			path[i][j] = j;
		}
	for (k = 0; k < graph.n; k++)
	{
		for (i = 0; i < graph.n; i++)
		{
			for (j = 0; j < graph.n; j++)
			{
				if (A[i][k] + A[k][j] < A[i][j])
				{
					A[i][j] = A[i][k] + A[k][j];
					path[i][j] = path[i][k];
				}
			}
		}
	}
	if (A[verx1][verx2] != Infinity)
	{
		cout << verx1 + 1 << "到" << verx2 + 1 << "的最短路径长度为:" << A[verx1][verx2] << ",";
		cout << "最短路径为:";
		cout << verx1 + 1;
		vertex = path[verx1][verx2];
		while (vertex != verx2)
		{
			cout << "->" << vertex + 1;
			vertex = path[vertex][verx2];
		}
		cout << "->" << verx2 + 1 << endl;
	}
	else
		cout << verx1 + 1 << "到" << verx2 + 1 << "无最短路径" << endl;
	if (A[verx2][verx1] != Infinity)
	{
		cout << verx2 + 1 << "到" << verx1 + 1 << "的最短路径长度为:" << A[verx2][verx1] << ",";
		cout << "最短路径为:";
		cout << verx2 + 1;
		vertex = path[verx2][verx1];
		while (vertex != verx1)
		{
			cout << "->" << vertex + 1;
			vertex = path[vertex][verx1];
		}
		cout << "->" << verx1 + 1 << endl;
	}
	else
		cout << verx2 + 1 << "到" << verx1 + 1 << "无最短路径" << endl;
}//FloydPro算法
void TwoVertex(MTGraph graph)//调用FloydPro的函数
{
	int verx1, verx2;
	cout << "请分别输入两个顶点:" << endl;
	cin >> verx1 >> verx2;
	FloydPro(graph,verx1-1,verx2-1);
}
int main()
{
	MTGraph graph;
	graph.n = 0; graph.e = 0;
	ReadInMatirx(graph);
	for (int i = 0; i < graph.n; i++)
	{
		for (int j = 0; j < graph.n; j++)
			printf("%7d", graph.Matrix[i][j]);
		cout << endl;
	}
	cout << "顶点表为:";
	for (int k = 0; k < graph.n; k++)
		cout << graph.vertex[k] + 1 << " ";
	cout << endl;
	DijkstrabyHeapPro(graph);
	cout << "Floyd算法求所有顶点对间最短路径:"<<endl;
	Floyd(graph);
	cout << "Warshall算法求可达矩阵:"<<endl;
	Warshall(graph);
	cout << "调整后的Dijkstra算法求所有点到目标点的最短路径:"<<endl;
	Trans(graph);
	cout << "Floyed算法求最短路径:" << endl;
	TwoVertex(graph);
	return 0;
}
  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值