标签:blog http io ar os sp for 文件 on
Dikstra算法解决的是有向图上单源最短路径问题(无向图可以看成有相反的两条有向边),且要求边的权重都是非负值。
算法导论用了很多引理,性质来证明Dijstra算法的正确性,这里不说了,也表达不明白,只说我理解的过程。
有一个图G( V,E) ,选定一个源点s,维护一个集合Q=V-s, Q中点有一个d值表示此时从s到该点的已知距离,s.d=0 ;初始化都为正无穷,表明不可达。然后对s点所连接的点(设为点集M)进行松弛操作,就是设点m属于M,
m.d > s.d+ w(s,m) 则更新 m.d=s.d+w(s,m) 将其慢慢收敛。
Q中的点能够松弛的松弛完之后,取出此时d值最小的点的,把它看成上面的s点,对其所连接点的进行松弛操作,然后再取点,重复直到Q集合为空。s点到所有点的最短路径就找到了。
再来说说实现的问题:
1.Q集合用数组来实现,我在实现的时候Q数组存在的是对应点的d值,如果这个点已经被取出则变为1;这样所有的开销都在去最小值上了,因为每次都要扫描整个数组。取点要V次,一次扫描O(V) ,所以总运行时间O(V^2);
2.Q集合用优先队列实现,但是因为要有decrease-key操作,所以貌似不能用标准库的优先队列。所以只能自己实现一个最小堆的优先队列。因为松弛的时候是找到点,然后又要去更改Q中的对应点的d值,然后在进行调整,所以要有互相指向的句柄。
取点V次,每次Extract-min花费O(lgV)
decrease-key E次 ,每次花费 O(lgV)
总花费 O( (E+V)lgV )
数组方法的时候,代码不贴出来来了,带句柄的数组建最小优先队列的方法我贴出来,其实句柄就是一个指向元素在数组堆中的位置,因为要建堆,维护堆,取出最小值等操作会因为位置变化,所以要在几个操作里面更新句柄。
Dijkstra.cpp
#include<iostream> #include<fstream> #include<queue> #include"MiniPriorityQueue.h" using namespace std; //Dijkstra算法解决的是带权重的单源最短路径问题,要求所有边的权重都是非负值 //图的结构,点的结构,查看.h文件。 //最小堆的结构实现也在.h文件 void printGraph(Graph g) { for(int i=1;i!=g.n+1;++i){ adjnode p=g.Adj[i]; while(p!=NULL){ cout<<p->adjvex<<" : "<<p->weight<<" "; p=p->nextvex; } cout<<endl; } } void printPaht1(Graph g,int s,int v){ //打印路线递归 if(s==v)cout<<s<<" "; else if(v==g.vexs[v].predecessor){ cout<<"结点"<<s<<"到结点"<<v<<"不可达"<<endl; } else{ printPaht1(g,s,g.vexs[v].predecessor); cout<<v<<" "; } } void printPath2(Graph g,int s) { // cout<<"源节点"<<s<<endl; for(int i=1;i!=g.n+1;++i) { if(i!=s){ int t=g.vexs[i].predecessor; if(t==i){ cout<<"结点"<<i<<" 不可达"<<endl;continue; } cout<<"结点"<<i<<" "<<"路径长度 "<<g.vexs[i].d<<" 路径过程为: "<<i<<" "; while(t!=s&&t!=i){ cout<<t<<" "; t=g.vexs[t].predecessor; } cout<<s<<endl; } } } void Dijkstra(Graph g,int s) { //Dijstra方法的主代码。 for(int i=1;i!=g.n+1;++i){ //初始化 g.vexs[i].d=100000; g.vexs[i].predecessor=i; g.vexs[i].n=i; } g.vexs[s].d=0; //源点的初始化 g.vexs[s].n=s; g.vexs[s].predecessor=s; MiniPriorityQueue Q(g); //堆初始化, Q.BuildMinHeap(g);//建堆操作,需要更新句柄 vector<vex>S; //取出的点放入S while(S.size()!=g.n){ vex t=Q.ExtractMin(g); //从堆中取出d值最小的元素, 需要更新句柄, int th=t.n; //vex中的n 实际就是从堆中元素指回结点的句柄 S.push_back(g.vexs[th]); //插入S中 for(adjnode p=g.Adj[th];p!=NULL;p=p->nextvex) //松弛操作。 { int m=p->adjvex; if(g.vexs[m].d> p->weight+g.vexs[th].d){ g.vexs[m].d=p->weight+g.vexs[th].d; g.vexs[m].predecessor=th; //接着要去更新堆 int h=g.vexs[m].handle; Q.DecreaseKey(h, g.vexs[m].d ,g); } } } cout<<"\n\n"<<endl; for(int i=1;i!=g.n+1;++i) {printPaht1(g,s,i); cout<<" 代价:"<<g.vexs[i].d<<endl; } cout<<endl; printPath2(g,s); } int main() { Graph G; CreateGraph(G); //建图 printGraph(G);// 打印图 Dijkstra(G,1); //源点为1,找单源最短路径 return 0;}
MiniPriorityQueue.h:
#ifndef MINIPRIORITYQUEUE_H_INCLUDED #define MINIPRIORITYQUEUE_H_INCLUDED #include<iostream> #include<fstream> #include<queue> using namespace std; class node { //邻接链表结构 public: node():adjvex(0),nextvex(NULL){} int adjvex; int weight; //邻接链表结点直接带权重值 node *nextvex; }; class vex { //点结构 public: int d; int predecessor; //前驱 int n; //表明自己几号点 int handle; //句柄,指示在堆的位置,堆由数组建立 }; typedef node * adjnode; class Graph { //图结构 public: int n;//顶点个数 adjnode *Adj; vex * vexs; }; void CreateGraph(Graph &g) { ifstream infile("test.txt"); cin.rdbuf(infile.rdbuf()); if(g.Adj==NULL)cout<<1<<endl; cout<<"请输入顶点数"<<endl; int n,x,w; cin>>n; g.Adj=new adjnode[n+1];// 0号不用 g.vexs=new vex[n+1]; g.n=n; cout<<"依次输入与每个顶点相邻的顶点号和权值,以-1为结束"<<endl; for(int i=1;i!=n+1;++i){ g.Adj[i]=new node; adjnode ne=g.Adj[i]; int ex=0; while(true){ cin>>x>>w; if(x==-1) break; else if(ex!=0){ ne->nextvex=new node; ne=ne->nextvex; } ++ex; ne->adjvex=x; ne->weight=w; }//while } } class MiniPriorityQueue{ //自建堆 public: MiniPriorityQueue(Graph g){ //初始化 Q=new vex[g.n+1]; length=g.n; for(int i=1;i!=g.n+1;++i){ Q[i]=g.vexs[i]; g.vexs[i].handle=i; //更新句柄 } } void MinHeapFix(int i,Graph &g); //维护堆的性质 void BuildMinHeap(Graph &g);//建堆 void DecreaseKey(int j,int k,Graph &g); //更新d值 vex ExtractMin(Graph &g){ //取出最小值, vex t=Q[1]; g.vexs[Q[length].n].handle=1;// 更新句柄 Q[1]=Q[length]; length--; MinHeapFix(1,g); return t; } public: vex* Q; int length; }; void MiniPriorityQueue::DecreaseKey(int j,int k,Graph &g){ //更新d值 Q[j].d=k; int i=j; while(i>1 && Q[i/2].d>Q[i].d){ g.vexs[Q[i/2].n].handle=i; g.vexs[Q[i].n ].handle=i/2; swap(Q[i/2],Q[i]); i=i/2; } } void MiniPriorityQueue::MinHeapFix(int i,Graph &g){ //堆建立过程中也要时刻更新句柄 int l=i*2; int r=i*2+1; int minest; if(l<=length&& Q[l].d<Q[i].d){ minest=l; } else{ minest=i; } if(r<=length&&Q[r].d < Q[minest].d){ minest=r; } if(minest!=i){ g.vexs[ Q[minest].n ].handle=i; //更新句柄。 g.vexs[Q[i].n ].handle=minest; swap(Q[minest],Q[i]); MinHeapFix(minest,g); } } void MiniPriorityQueue::BuildMinHeap(Graph &g){ int start=length/2; for(;start>=1;--start){ MinHeapFix(start,g); } } #endif // MINIPRIORITYQUEUE_H_INCLUDED
test.txt:
5
2 10 4 5 -1 -1
3 1 4 2 -1 -1
5 4 -1 -1
2 3 3 9 5 2 -1 -1
3 6 1 7 -1 -1
图的图片:
s=1 ,t=2 ,x=3 , y=4 ,z =5
运行结果:
最后我们可以发现,广度优先算法其实就是Dijkstra算法的简化,其中边的权重全部变为1,Q队列用先进先出队列实现,
标签:blog http io ar os sp for 文件 on
原文地址:http://blog.csdn.net/y519476132/article/details/40899561