【C++】Dijkstra算法解决最短路径问题

最短路径问题

问题描述:平面上有n个点(n<=100),每个点的坐标均在-10000~10000之间。其中的一些点之间有连线。若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点间的直线距离。现在的任务是找出从一点到另一点之间的最短距离。

输入:

第1行为整数n,表示图中顶点的个数。

第2行到第n+1行(共n行),每行两个整数x和y,描述了一个点的坐标(以一个空格分隔)。

第n+2行为一个整数m,表示图中连线的个数。

此后的m行,每行描述一条连线,由两个整数i和j组成,表示第i个点和第j个点之间有连线。

最后一行:两个整数s和t,分别表示源点和目标点。

输出:

仅1行,一个实数(保留两位小数),表示从s到t的最短路径长度。

输入样例:

5

0    0

2    0

2    2

0    2

3    1

5

1    2

1    3

1    4

2    5

3    5

1    5

输出样例:

3.41

代码示例👇

//author:Mitchell_Donovan
//date:5.18
#include<iostream>
#include<queue>
#include<iomanip>//用于保留两位小数输出
using namespace std;

//边类
class Edge {
public:
	int from, to;
	double weight;
	Edge() {
		from = -1;
		to = -1;
		weight = 0;
	}
	Edge(int fromValue, int toValue, double weightValue) {
		from = fromValue;
		to = toValue;
		weight = weightValue;
	}
};

//图类
class Graph {
public:
	int numVertex;
	int numEdge;
	int* Mark;//标记图中顶点是否被访问过
	int* Indegree;//存放图中顶点的入度
	Graph(int num) {
		numVertex = num;
		numEdge = 0;
		Indegree = new int[numVertex];
		Mark = new int[numVertex];
		for (int i = 0; i < numVertex; i++) {
			Mark[i] = 0;//0表示未访问过
			Indegree[i] = 0;//入度设为0
		}
	}
	~Graph() {
		delete[]Mark;
		delete[]Indegree;
	}
	//判断是否为边
	bool isEdge(Edge oneEdge) {
		if (oneEdge.weight > 0 && oneEdge.weight < INFINITY && oneEdge.to >= 0) {
			return true;
		}
		else {
			return false;
		}
	}
	//访问
	void Visit(Graph& G, int v) {
		cout << v + 1 << " ";
		//cout << G.data[v];
	}
};

//用相邻矩阵表示图
class Graphm :public Graph {//类继承
private:
	double** matrix;//指向相邻矩阵的指针
public:
	Graphm(int num) :Graph(num) {
		matrix = (double**)new double* [numVertex];//申请二维数组空间
		for (int i = 0; i < numVertex; i++) {
			matrix[i] = new double[numVertex];
		}
		for (int i = 0; i < numVertex; i++) {//相邻矩阵初始化
			for (int j = 0; j < numVertex; j++) {
				matrix[i][j] = 0;
			}
		}
	}
	~Graphm() {
		for (int i = 0; i < numVertex; i++) {
			delete[]matrix[i];
		}
		delete[] matrix;
	}
	//返回顶点的第一条边
	Edge firstEdge(int oneVertex) {
		Edge myEdge;
		myEdge.from = oneVertex;
		for (int i = 0; i < numVertex; i++) {
			if (matrix[oneVertex][i] != 0) {
				myEdge.to = i;
				myEdge.weight = matrix[oneVertex][i];
				break;
			}
		}
		return myEdge;
	}
	//返回与已知边相同顶点的下一条边
	Edge nextEdge(Edge preEdge) {
		Edge myEdge;
		myEdge.from = preEdge.from;
		if (preEdge.to >= numVertex) {//不存在下一条边
			return myEdge;
		}
		for (int i = preEdge.to + 1; i < numVertex; i++) {
			if (matrix[preEdge.from][i] != 0) {
				myEdge.to = i;
				myEdge.weight = matrix[preEdge.from][i];
				break;
			}
		}
		return myEdge;
	}
	//为图设置一条边
	void setEdge(int from, int to, double weight) {
		if (matrix[from][to] <= 0) {//如果原边不存在
			numEdge++;
			Indegree[to]++;
		}
		matrix[from][to] = weight;
	}
	//删除图的一条边
	void delEdge(int from, int to) {
		if (matrix[from][to] > 0) {//如果原边存在
			numEdge--;
			Indegree[to]--;
		}
		matrix[from][to] = 0;
	}
};

//结构体Dist用于保存最短路径信息
struct Dist {
	int index;//顶点的索引项
	double length;//当前最短路径长度
	int pre;//路径最后经过的顶点
};

//递归函数print用于输出具体路径
void print(int from, int to, Dist*& D) {
	if (D[to].pre == from) {
		cout << from << "->" << to;
	}
	else {
		print(from, D[to].pre, D);
		cout << "->" << to;
	}
}

//Dijkstra算法求源点s到其他各点的最短路径
void Dijkstra(Graphm& G, int s, int to) {
	Dist* D = new Dist[G.numVertex];
	for (int i = 0; i < G.numVertex; i++) {//初始化Mark数组和D数组
		G.Mark[i] = 0;
		D[i].index = i;
		D[i].length = INFINITY;
		D[i].pre = s;
	}
	D[s].length = 0;//源点到自身长度设置为0
	//定义D.length小的优先级高的运算符
	struct cmp {
		bool operator ()(const Dist& a, const Dist& b) {
			return a.length > b.length;
		}
	};
	priority_queue<Dist, vector<Dist>, cmp> minHeap;//最小堆(优先队列)用于找出最短路径
	minHeap.push(D[s]);
	for (int i = 0; i < G.numVertex; i++) {
		bool FOUND = false;
		Dist d;
		while (!minHeap.empty()) {
			d = minHeap.top();//获得到s路径长度最小的顶点
			minHeap.pop();
			if (G.Mark[d.index] == 0) {//如果该标记点未访问过
				FOUND = true;
				break;
			}
		}
		if (!FOUND) {//如果没有符合条件的最短路径则跳出本次循环
			break;
		}
		int v = d.index;
		G.Mark[v] = 1;
		//加入v以后要刷新D中v的邻接点的最短路径长度
		for (Edge e = G.firstEdge(v); G.isEdge(e); e = G.nextEdge(e)) {
			if (D[e.to].length > (D[v].length + e.weight)) {
				D[e.to].length = D[v].length + e.weight;
				D[e.to].pre = v;
				minHeap.push(D[e.to]);
			}
		}
	}
	//打印出结果
	cout << "最短路径长度为:" << fixed << setprecision(2) << D[to].length << endl;//保留两位小数输出
	cout << "具体路径为:";
	print(s, to, D);
}



int main() {
	int n, m;
	cout << "顶点个数:";
	cin >> n;
	Graphm test(n);
	struct position {
		int x;
		int y;
	};
	position* pos = new position[n];
	for (int i = 0; i < n; i++) {
		cout << "第" << i + 1 << "个点的坐标:";
		cin >> pos[i].x;
		cin >> pos[i].y;
	}
	cout << "图中连线的个数:";
	cin >> m;
	int from, to;
	for (int i = 0; i < m; i++) {
		cout << "第" << i + 1 << "条边的起点和终点:";
		cin >> from;
		cin >> to;
		double length = sqrt(pow(pos[to - 1].x - pos[from - 1].x, 2) + pow(pos[to - 1].y - pos[from - 1].y, 2));
		test.setEdge(from - 1, to - 1, length);
		test.setEdge(to - 1, from - 1, length);
	}
	cout << "请输入源点和目标点:";
	cin >> from;
	cin >> to;
	Dijkstra(test, from - 1, to - 1);
}

输出示例👇

补充参考

C++优先队列自定义排序总结

C / C++ 保留两位小数(setprecision(n)的一些用法总结)

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Dijkstra算法是一种用于解决最短路径问题贪心算法。它的基本思想是以起始点为心向外层层扩展,直到扩展到终点为止。Dijkstra算法解决非负权值的单源最短路径问题上非常有效。下面是Dijkstra算法C++代码实现: ```c++ #include <iostream> #include <vector> #include <queue> #include <cstring> using namespace std; const int INF = 0x3f3f3f3f; // 表示正无穷 int n, m; // n表示点数,m表示边数 int dis[MAX]; // dis数组用于存储起点到各个点的最短距离 bool vis[MAX]; // vis数组用于判断该点是否已经被访问 struct Edge { int to; // 终点 int cost; // 边权 }; vector<Edge> G[MAX]; // G为邻接表,用于存储的边 void dijkstra(int s) { priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; memset(dis, INF, sizeof(dis)); // 将dis数组初始化为正无穷 memset(vis, false, sizeof(vis)); // 将vis数组初始化为false dis[s] = 0; // 起点到自己的距离为0 q.push(make_pair(dis[s], s)); // 将起点入队 while (!q.empty()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = true; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i].to; int cost = G[u][i].cost; if (dis[v] > dis[u] + cost) { dis[v] = dis[u] + cost; q.push(make_pair(dis[v], v)); } } } } int main() { cin >> n >> m; // 输入点数和边数 for (int i = 1; i <= m; i++) { int u, v, w; cin >> u >> v >> w; G[u].push_back((Edge){v, w}); G[v].push_back((Edge){u, w}); // 无向需要反向建边 } dijkstra(1); // 以1号点为起点求最短路径 for (int i = 1; i <= n; i++) { cout << dis[i] << " "; } cout << endl; return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mitch311

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值