码迷,mamicode.com
首页 > 编程语言 > 详细

算法导论-第24章 Dijkstra算法

时间:2014-11-07 23:32:47      阅读:333      评论:0      收藏:0      [点我收藏+]

标签: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

图的图片:

bubuko.com,布布扣

s=1   ,t=2  ,x=3 , y=4 ,z =5


运行结果:

bubuko.com,布布扣

最后我们可以发现,广度优先算法其实就是Dijkstra算法的简化,其中边的权重全部变为1,Q队列用先进先出队列实现,


算法导论-第24章 Dijkstra算法

标签:blog   http   io   ar   os   sp   for   文件   on   

原文地址:http://blog.csdn.net/y519476132/article/details/40899561

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!