网站建设目的和功能定位,宁波有做网站的地方吗,青岛网站建设搜q.479185700,外汇平台网站建设文章目录 一、最短路径二、单源最短路径--Dijkstra算法三、单源最短路径--Bellman-Ford算法四、多源最短路径--Floyd-Warshall算法 一、最短路径
最短路径问题#xff1a;从在带权有向图G中的某一顶点出发#xff0c;找出一条通往另一顶点的最短路径#xff0c;最短也就是沿… 文章目录 一、最短路径二、单源最短路径--Dijkstra算法三、单源最短路径--Bellman-Ford算法四、多源最短路径--Floyd-Warshall算法 一、最短路径
最短路径问题从在带权有向图G中的某一顶点出发找出一条通往另一顶点的最短路径最短也就是沿路径各边的权值总和达到最小。
二、单源最短路径–Dijkstra算法
单源最短路径问题给定一个图G ( V E ) G(VE)G(VE)求源结点s ∈ V s∈Vs∈V到图中每个结点v ∈ V v∈Vv∈V的最短路径。Dijkstra算法就适用于解决带权重的有向图上的单源最短路径问题同时算法要求图中所有边的权重非负。一般在求解最短路径的时候都是已知一个起点和一个终点所以使用Dijkstra算法求解过后也就得到了所需起点到终点的最短路径。
针对一个带权有向图G将所有结点分为两组S和QS是已经确定最短路径的结点集合在初始时为空初始时就可以将源节点s放入毕竟源节点到自己的代价是0Q 为其余未确定最短路径的结点集合每次从Q 中找出一个起点到该结点代价最小的结点u 将u 从Q 中移出并放入S 中对u 的每一个相邻结点v 进行松弛操作。松弛即对每一个相邻结点v 判断源节点s到结点u 的代价与u 到v 的代价之和是否比原来s 到v 的代价更小若代价比原来小则要将s 到v 的代价更新为s 到u 与u 到v 的代价之和否则维持原样。如此一直循环直至集合Q 为空即所有节点都已经查找过一遍并确定了最短路径至于一些起点到达不了的结点在算法循环后其代价仍为初始设定的值不发生变化。Dijkstra算法每次都是选择V-S中最小的路径节点来进行更新并加入S中所以该算法使用的是贪心策略。
Dijkstra算法存在的问题是不支持图中带负权路径如果带有负权路径则可能会找不到一些路径的最短路径。 代码实现
// 临接矩阵
namespace Matrix
{template class V, class W, W MAX_W INT_MAX, bool Direction falseclass Graph{typedef GraphV, W, MAX_W, Direction Self;private:std::vectorV _vertexs; // 顶点集合std::mapV, size_t _vIndexMap; // 顶点的下标映射关系std::vectorstd::vectorW _matrix; // 存储边集合的矩阵void PrintShortPath(const V src, const std::vectorW dist, const std::vectorint pPath){size_t srci GetVertexIndex(src);size_t n _vertexs.size();for (size_t i 0; i n; i){if (i ! srci){// 找出i顶点的路径std::vectorint path;size_t parenti i;while (parenti ! srci){path.push_back(parenti);parenti pPath[parenti];}path.push_back(srci);reverse(path.begin(), path.end());for (auto index : path){std::cout _vertexs[index] -;}std::cout 权值和 dist[i] std::endl;}}}// 顶点个数是N - 时间复杂度ON^2空间复杂度ONvoid Dijkstra(const V src, std::vectorW dist, std::vectorint pPath){size_t srci GetVertexIndex(src);int n _vertexs.size();dist.resize(n, MAX_W);pPath.resize(n, -1);dist[srci] 0;pPath[srci] srci;// 已经确定最短路径的顶点集合std::vectorbool S(n, false);// n个节点每个节点都要作为起点for (int i 0; i n; i){// 选最短路径顶点且不在S更新其他路径int u 0;W min MAX_W;for (int j 0; j n; j){if (S[i] false dist[i] min){u i;min dist[i];}}S[u] true;// 松弛更新u连接顶点v srci-u u-v srci-v 更新for (int v 0; v n; v){if (S[v] false _matrix[u][v] ! MAX_W dist[u] _matrix[u][v] dist[v]){dist[v] dist[u] _matrix[u][v];pPath[v] u;}}}}};
}测试代码
void TestGraphDijkstra()
{const char *str syztx;Graphchar, int, INT_MAX, true g(str, strlen(str));g.AddEdge(s, t, 10);g.AddEdge(s, y, 5);g.AddEdge(y, t, 3);g.AddEdge(y, x, 9);g.AddEdge(y, z, 2);g.AddEdge(z, s, 7);g.AddEdge(z, x, 6);g.AddEdge(t, y, 2);g.AddEdge(t, x, 1);g.AddEdge(x, z, 4);std::vectorint dist;std::vectorint parentPath;g.Dijkstra(s, dist, parentPath);g.PrintShortPath(s, dist, parentPath);// // 图中带有负权路径时贪心策略则失效了。// // 测试结果可以看到s-t-y之间的最短路径没更新出来// const char *str sytx;// Graphchar, int, INT_MAX, true g(str, strlen(str));// g.AddEdge(s, t, 10);// g.AddEdge(s, y, 5);// g.AddEdge(t, y, -7);// g.AddEdge(y, x, 3);// std::vectorint dist;// std::vectorint parentPath;// g.Dijkstra(s, dist, parentPath);// g.PrintShortPath(s, dist, parentPath);
}三、单源最短路径–Bellman-Ford算法
Dijkstra算法只能用来解决正权图的单源最短路径问题但有些题目会出现负权图。这时这个算法就不能帮助我们解决问题了而bellman—ford算法可以解决负权图的单源最短路径问题。它的优点是可以解决有负权边的单源最短路径问题而且可以用来判断是否有负权回路。它也有明显的缺点它的时间复杂度 O(NE) (N是点数E是边数)普遍是要高于Dijkstra算法O(N²)的。像这里如果我们使用邻接矩阵实现那么遍历所有边的数量的时间复杂度就是O(N^3)这里也可以看出来Bellman-Ford就是一种暴力求解更新。 代码实现
// 临接矩阵
namespace Matrix
{template class V, class W, W MAX_W INT_MAX, bool Direction falseclass Graph{typedef GraphV, W, MAX_W, Direction Self;private:std::vectorV _vertexs; // 顶点集合std::mapV, size_t _vIndexMap; // 顶点的下标映射关系std::vectorstd::vectorW _matrix; // 存储边集合的矩阵void PrintShortPath(const V src, const std::vectorW dist, const std::vectorint pPath){size_t srci GetVertexIndex(src);size_t n _vertexs.size();for (size_t i 0; i n; i){if (i ! srci){// 找出i顶点的路径std::vectorint path;size_t parenti i;while (parenti ! srci){path.push_back(parenti);parenti pPath[parenti];}path.push_back(srci);reverse(path.begin(), path.end());for (auto index : path){std::cout _vertexs[index] -;}std::cout 权值和 dist[i] std::endl;}}}// 时间复杂度O(N^3) 空间复杂度ONbool BellmanFord(const V src, std::vectorW dist, std::vectorint pPath){size_t n _vertexs.size();size_t srci GetVertexIndex(src);// vectorW dist,记录srci-其他顶点最短路径权值数组dist.resize(n, MAX_W);// vectorint pPath 记录srci-其他顶点最短路径父顶点数组pPath.resize(n, -1);// 先更新srci-srci为缺省值dist[srci] W();// 总体最多更新n轮for (int k 0; k n; k){bool update false;std::cout 更新第: k 轮 std::endl;for (int i 0; i n; i){for (int j 0; j n; j){// srci - i i - jif (_matrix[i][j] ! MAX_W dist[i] _matrix[i][j] dist[j]){update true;std::cout _vertexs[i] - _vertexs[j] : _matrix[i][j] std::endl;dist[j] dist[i] _matrix[i][j];pPath[j] i;}}}// 如果这个轮次中没有更新出更短路径那么后续轮次就不需要再走了if (update false)break;}// 还能更新就是带负权回路for (int i 0; i n; i){for (int j 0; j n; j){if (_matrix[i][j] ! MAX_W dist[i] _matrix[i][j] dist[j]){return false;}}}return true;}};
}测试代码
void TestGraphBellmanFord()
{// const char *str syztx;// Graphchar, int, INT_MAX, true g(str, strlen(str));// g.AddEdge(s, t, 6);// g.AddEdge(s, y, 7);// g.AddEdge(y, z, 9);// g.AddEdge(y, x, -3);// g.AddEdge(z, s, 2);// g.AddEdge(z, x, 7);// g.AddEdge(t, x, 5);// g.AddEdge(t, y, 8);// g.AddEdge(t, z, -4);// g.AddEdge(x, t, -2);// std::vectorint dist;// std::vectorint parentPath;// g.BellmanFord(s, dist, parentPath);// g.PrintShortPath(s, dist, parentPath);// 微调图结构带有负权回路的测试const char *str syztx;Graphchar, int, INT_MAX, true g(str, strlen(str));g.AddEdge(s, t, 6);g.AddEdge(s, y, 7);g.AddEdge(y, x, -3);g.AddEdge(y, z, 9);g.AddEdge(y, x, -3);g.AddEdge(y, s, 1); // 新增g.AddEdge(z, s, 2);g.AddEdge(z, x, 7);g.AddEdge(t, x, 5);g.AddEdge(t, y, -8); // 更改g.AddEdge(t, z, -4);g.AddEdge(x, t, -2);std::vectorint dist;std::vectorint parentPath;if (g.BellmanFord(s, dist, parentPath))g.PrintShortPath(s, dist, parentPath);elsestd::cout 带负权回路 std::endl;
}四、多源最短路径–Floyd-Warshall算法
Floyd-Warshall算法是解决任意两点间的最短路径的一种算法。
Floyd算法考虑的是一条最短路径的中间节点即简单路径p{v1,v2,…,vn}上除v1和vn的任意节点。
设k是p的一个中间节点那么从i到j的最短路径p就被分成i到k和k到j的两段最短路径p1p2。p1是从i到k且中间节点属于{12…k-1}取得的一条最短路径。p2是从k到j且中间节点属于{12…k-1}取得的一条最短路径。 即Floyd算法本质是三维动态规划D[i][j][k]表示从点i到点j只经过0到k个点最短路径然后建立起转移方程然后通过空间优化优化掉最后一维度变成一个最短路径的迭代算法最后即得到所以点的最短路。 代码实现
// 临接矩阵
namespace Matrix
{template class V, class W, W MAX_W INT_MAX, bool Direction falseclass Graph{typedef GraphV, W, MAX_W, Direction Self;private:std::vectorV _vertexs; // 顶点集合std::mapV, size_t _vIndexMap; // 顶点的下标映射关系std::vectorstd::vectorW _matrix; // 存储边集合的矩阵void PrintShortPath(const V src, const std::vectorW dist, const std::vectorint pPath){size_t srci GetVertexIndex(src);size_t n _vertexs.size();for (size_t i 0; i n; i){if (i ! srci){// 找出i顶点的路径std::vectorint path;size_t parenti i;while (parenti ! srci){path.push_back(parenti);parenti pPath[parenti];}path.push_back(srci);reverse(path.begin(), path.end());for (auto index : path){std::cout _vertexs[index] -;}std::cout 权值和 dist[i] std::endl;}}}void FloydWarshall(std::vectorstd::vectorW vvDist, std::vectorstd::vectorint vvpPath){size_t n _vertexs.size();vvDist.resize(n);vvpPath.resize(n);// 初始化权值和路径矩阵for (int i 0; i n; i){vvDist[i].resize(n, MAX_W);vvpPath[i].resize(n, -1);}for (int i 0; i n; i){for (int j 0; j n; j){if (_matrix[i][j] ! MAX_W){vvDist[i][j] _matrix[i][j];vvpPath[i][j] i;}if (i j){vvDist[i][j] W();}}}// abcdef a {} f || b {} c// 最短路径的更新i- {其他顶点} -jfor (int k 0; k n; k){for (int i 0; i n; i){for (int j 0; j n; j){// i - j i - k k - j// k 作为的中间点尝试去更新i-j的路径if (vvDist[i][k] ! MAX_W vvDist[k][j] ! MAX_W vvDist[i][k] vvDist[k][j] vvDist[i][j]){vvDist[i][j] vvDist[i][k] vvDist[k][j];// 找跟j相连的上一个邻接顶点// 如果k-j 直接相连上一个点就kvvpPath[k][j]存就是k// 如果k-j 没有直接相连k-...-x-jvvpPath[k][j]存就是xvvpPath[i][j] vvpPath[k][j];}}}}// 打印权值和路径矩阵观察数据for (int i 0; i n; i){for (int j 0; j n; j){if (vvDist[i][j] MAX_W){printf(%3c, *);}else{printf(%3d, vvDist[i][j]);}}std::cout std::endl;}std::cout std::endl;for (size_t i 0; i n; i){for (size_t j 0; j n; j){printf(%3d, vvpPath[i][j]);}std::cout std::endl;}std::cout std::endl;}};
}测试代码
void TestFloydWarShall()
{const char *str 12345;Graphchar, int, INT_MAX, true g(str, strlen(str));g.AddEdge(1, 2, 3);g.AddEdge(1, 3, 8);g.AddEdge(1, 5, -4);g.AddEdge(2, 4, 1);g.AddEdge(2, 5, 7);g.AddEdge(3, 2, 4);g.AddEdge(4, 1, 2);g.AddEdge(4, 3, -5);g.AddEdge(5, 4, 6);std::vectorstd::vectorint vvDist;std::vectorstd::vectorint vvParentPath;g.FloydWarshall(vvDist, vvParentPath);// 打印任意两点之间的最短路径for (size_t i 0; i strlen(str); i){g.PrintShortPath(str[i], vvDist[i], vvParentPath[i]);}
}