最短路径
Dijkstra单源节点到其他所有节点的最短路径
#include<iostream> #include<vector> #include<queue> #include<stack> using namespace std; const int INFINIT=32767; struct tableItem{ int prevIndex; int dist; bool found; tableItem(int p=-1,int d=INFINIT,bool f=false): prevIndex(p),dist(d),found(f){} }; int main(){ int graph[7][7]={{0,2,INFINIT,1,INFINIT,INFINIT,INFINIT}, {INFINIT,0,INFINIT,3,10,INFINIT,INFINIT}, {4,INFINIT,0,INFINIT,INFINIT,5,INFINIT}, {INFINIT,INFINIT,2,0,2,8,4}, {INFINIT,INFINIT,INFINIT,INFINIT,0,INFINIT,6}, {INFINIT,INFINIT,INFINIT,INFINIT,INFINIT,0,INFINIT}, {INFINIT,INFINIT,INFINIT,INFINIT,INFINIT,1,0}}; vector<tableItem> table(7); table[0].prevIndex=0; table[0].dist=0; while(1){ int node=-1; int minDist=INFINIT; int i; for(i=0;i<7;i++){ if(table[i].found==false && table[i].dist<minDist){ minDist=table[i].dist; node=i; } } if(node==-1) break; table[node].found=true; for(i=0;i<7;i++){ if(graph[node][i]<INFINIT && graph[node][i]>0){ if(table[node].dist+graph[node][i]<table[i].dist){ if(table[i].found==true){ cerr<<"the graph include circulate route!"<<endl; throw; } table[i].prevIndex=node; table[i].dist=table[node].dist+graph[node][i]; } } } } int end=1; for(;end<7;end++){ int curr=end; stack<int> s; int prev; do{ prev=table[curr].prevIndex; s.push(prev); curr=prev; }while(prev!=0); while(!s.empty()){ cout<<s.top()<<"-->"; s.pop(); } cout<<end<<endl; } return 0; }
分析这个算法复杂度。图中节点数是V,边数为E。最外层的while循环要循环V次,每次把一个节点的found设为true,当所有节点的found为true时循环退出。每次循环内部都要从found为false的节点中找dist最小者。因此这个过程复杂度为。更新节点的dist时,每条边最多被更新一次,总计为。所以总的复杂度为。
如果是稠密图,,则上述算法复杂度为,上述算法基本上是最优的,其运行时间与边数成线性关系。
如果是稀疏图,上述算法就太慢了,我们把降下来。最外层的while循环要循环V次这是不可改变的,所以我们要缩短寻找最小dist的时间,可以使用优先队列或者配对堆。
寻找所有点对的最短路径
对于稀疏图而言,更快的算法是运行V趟使用优先队列的Dijkstra算法找到所有点对的最短路径,此时的复杂度为。
这里给出另一种动态规划的解法,其运算时间复杂度也为,它不是对Dijkstra算法的改进,但对于非常稠密的图可能更快,原因是它的循环更紧凑。如果存在一此负的边值但没有负的回路,那么这个算法也能正确运行,而Dijkstra算法此时是无效的。
用二维数组graph来存储图,顶点之间的最短路径用二维数组dist来存储。 初始把graph赋给dist。顶点到顶点的最短路径是:
程序是这样运行的:遍历,对于每一个k,更新所有的。
#include<iostream> #include<iomanip> using namespace std; const int INFINIT=32767; int main(){ int graph[7][7]={{0,2,INFINIT,1,INFINIT,INFINIT,INFINIT}, {INFINIT,0,INFINIT,3,10,INFINIT,INFINIT}, {4,INFINIT,0,INFINIT,INFINIT,5,INFINIT}, {INFINIT,INFINIT,2,0,2,8,4}, {INFINIT,INFINIT,INFINIT,INFINIT,0,INFINIT,6}, {INFINIT,INFINIT,INFINIT,INFINIT,INFINIT,0,INFINIT}, {INFINIT,INFINIT,INFINIT,INFINIT,INFINIT,1,0}}; int path[7][7]={-1}; int **dist=new int *[7]; for(int i=0;i<7;i++){ dist[i]=new int[7]; for(int j=0;j<7;j++) dist[i][j]=graph[i][j]; } for(int k=0;k<7;k++){ for(int i=0;i<7;i++){ for(int j=0;j<7;j++){ if(dist[i][k]+dist[k][j]<dist[i][j]){ dist[i][j]=dist[i][k]+dist[k][j]; path[i][j]=k; } } } } cout<<right; for(int i=0;i<7;i++){ for(int j=0;j<7;j++){ cout<<setw(8)<<dist[i][j]; } cout<<endl; } for(int i=0;i<7;i++) delete []dist[i]; delete []dist; return 0; }
指定两点间的最短路径
使用Dijkstra算法找顶点到顶点的最短路径,当的found为true时,算法就可以终止了。
最短路径的应用
字梯游戏:一个单词通过变换一个字母可以变为另外一个单词。例如我们可以通过一系列的单词变换将zero变为five:zero-->hero-->here-->hire-->fire-->five。
这是一个无权最短路径问题,每个词是一个顶点。如果两个顶点可以通过一个字母替换相互转换的话,它们之间就有一条(无向)边。
本文来自博客园,作者:高性能golang,转载请注明原文链接:https://www.cnblogs.com/zhangchaoyang/articles/1868539.html