zoukankan      html  css  js  c++  java
  • Dijkstra And Floyd C++

    一.Dijkstra 算法

    //在这里采用两种方式实现Dijkstra算法,经过测试两种方式:第一种:通过Dist【】让每一个顶点中Dist[]中寻找最短路径的点;第二种方式:从已经收录的顶点的相邻顶点中选择出最短路//径的的顶点,此种方式适合用于稠密图,减少点的遍历。


    #include<iostream> #include<malloc.h> using namespace std; #define INF (100000) //相连顶点的信息 typedef struct AdjNode{ int Weight; int CostValue; int Adj; AdjNode *next; AdjNode():Weight(NULL),CostValue(NULL),next(NULL){}; }PtrToAdjVNode; //表头信息,每个表头的节点必须要进行标记,对表头进行标记 typedef struct Vnode{ PtrToAdjVNode *FirstEdge; int Flag; }AdjLink[10]; typedef struct AdjGraph{ int VerterNum; int EdgeNum; AdjLink LG; }LGraph; LGraph *Graph=new LGraph; void Insert(int A,int B,int C,int D) { PtrToAdjVNode *Ptr=new PtrToAdjVNode,*Rear; Ptr->Adj=B; Ptr->Weight=C;Ptr->CostValue=D;Ptr->next=NULL; if(!Graph->LG[A].FirstEdge) { Graph->LG[A].FirstEdge=Ptr; } else { Rear=Graph->LG[A].FirstEdge; while(Rear->next!=NULL) { Rear=Rear->next; } Rear->next=Ptr; } } void CreateGraph(int VerNum,int EdgeNum) {//顶点从0开始 int i;Graph->VerterNum=VerNum;Graph->EdgeNum=EdgeNum; for(i=0;i<VerNum;i++) { Graph->LG[i].Flag=NULL; Graph->LG[i].FirstEdge=NULL; } int NN,MM,KK,CC; for(i=0;i<EdgeNum;i++) { cin>>NN>>MM>>KK>>CC; Insert(NN,MM,KK,CC); } } int Get_Weight(int Start,int FindNum) { PtrToAdjVNode *Rear=Graph->LG[Start].FirstEdge; int Value=INF; if(Start==FindNum) return 0; while(Rear) { if(Rear->Adj==FindNum) { Value=Rear->Weight; break; } else { Rear=Rear->next; } } return Value; } void Search(int prev[],int Start,int end) { //简单摆Dijkstra算法遍历 int i=end,j=0;int *rev=new int[Graph->VerterNum]; //倒序的路径 for(i=end;i!=Start;i=prev[i]) { cout<<"["<<i<<"] "; rev[j++]=i; } cout<<i<<endl; } void Dijkstra(int dist1[],int prev1[],int Vstart,int Vend) { //防止出现两条相等路径,最外面只将从Start---End点上的最短路径上顶点在表头上标记 //最外面只标记最短路径,最好只标记与顶点相邻的分叉点 /***第一步计算出S点与相邻点的dist,同时初始化*/ int prev[10],dist[10],flag[10]; int i;//int *flag=new int[Graph->VerterNum]; for(i=0;i<Graph->VerterNum;i++) { flag[i]=0; dist[i]=Get_Weight(Vstart,i);//只能够得到他么最短路径 if(dist[i]<INF) prev[i]=Vstart; else prev[i]=-1; } //接下来是Dijkstra 核心,从U集合中选取一个最短路径的点加入S集合 //同时更新比较经过k 点与不经过K点到k 的相邻点上距离。 prev[Vstart]=Vstart;flag[Vstart]=1; int m,n,kIndex,j; int MIndex=Vstart; int Min,Tmp; int Min1=INF,K1; for(m=0;m<Graph->VerterNum;m++)//大循环保证每个点都能遍历 { Min=INF;Min1=INF; //从U中挑选出离S集合最近的点,通过比较,将各个顶点与S中点相比较 for(n=0;n<Graph->VerterNum;n++) { //从没有遍历的点中找到最短路径的点 if(Graph->LG[n].FirstEdge &&Graph->LG[n].Flag==false && !flag[n] ) { if(dist[n]<Min) { Min=dist[n]; kIndex=n; } } } /**上面步骤主要是从相近点中找到最短路径的点,dist存储各个点情况,只有路径就可以到达,保证找到最终点*/ //如果找到最终点刚好在最短路径上,可以用之下方法 //只从点的相邻点中找路径,flag表示已经遍历dist存储各个点情况 PtrToAdjVNode *Rear=Graph->LG[MIndex].FirstEdge; //从相邻点中找最短路径,保证最终点在路径上,但是找出图中最短的点 while(Rear) { if(flag[Rear->Adj]==0) { if(dist[Rear->Adj]<Min1) { Min1=dist[Rear->Adj]; MIndex=Rear->Adj; } } Rear=Rear->next; } flag[MIndex]=1; //不断更新dist同prev for(j=0;j<Graph->VerterNum;j++) { Tmp=Get_Weight(MIndex,j); Tmp=(Tmp==INF)?INF:(Min1+Tmp); if(!flag[j] && Graph->LG[j].Flag==false && dist[j]>Tmp)//只有在修改时候才能进行prev修改 { dist[j]=Tmp; prev[j]=MIndex; } } } //对路径的回溯 Search(prev,Vstart,Vend); } int main() { int VerNum,EdgeNum,VerStart,Verend; cin>>VerNum>>EdgeNum>>VerStart>>Verend; CreateGraph(VerNum,EdgeNum); //选择出最短路径,如果有相同最短路径在计算出最小花费 int *dist=new int[VerNum]; int *prev=new int[VerNum]; Dijkstra(dist,prev,VerStart,Verend); return 0; }

     

    Dijkstra算法主要是单源路径的最短算法,计算的是起始点到其他顶点的最短路径;算法具体:

    1.S集合中表示收录的最短路径的点集合,U剩余点的集合,Dist[]表示起始点到其他点最短路径,每一次从dist中选择出最短路径的点e(这种方式能够选出多条路径);

    2.对dist和prev矩阵通过新加入的点进行更新。

    现在只是解决了最短路径的问题,单路径中出现相等路径怎么用Dijkstra算法计算出相同不同顶点有相同路径算法。

    下面这个算法针对计算多条路径:方法,在表头列表中增加一项Prev,记录遍历整个的过程,特别记录到达某点时候dist[i]=Tmp 处值,在这里最有可能会发生相同路径的特需点

    模拟图

    在列表prev中贮存Start到此点中最短路径的前一个点

    #include<iostream>
     #include<malloc.h>
     using namespace std;
     #define INF (100000)
    
     //相连顶点的信息
     typedef struct AdjNode{
        int Weight;
        int CostValue;
        int Adj;
        AdjNode *next;
    AdjNode():Weight(NULL),CostValue(NULL),next(NULL){};
     }PtrToAdjVNode;
    
     //表头信息,每个表头的节点必须要进行标记,对表头进行标记
     typedef struct Vnode{
         PtrToAdjVNode *FirstEdge;
         int prev[5];//记录与此节点距离最近的父节点
         int pcnt;
     }AdjLink[10];
    
     typedef struct AdjGraph{
     int VerterNum;
     int EdgeNum;
     AdjLink LG;
     }LGraph;
    
     LGraph *Graph=new LGraph;
     void Insert(int A,int B,int C,int D)
     {
        PtrToAdjVNode *Ptr=new PtrToAdjVNode,*Rear;
        Ptr->Adj=B;
        Ptr->Weight=C;Ptr->CostValue=D;Ptr->next=NULL;
        if(!Graph->LG[A].FirstEdge)
        {
            Graph->LG[A].FirstEdge=Ptr;
        }
        else
        {
            Rear=Graph->LG[A].FirstEdge;
            while(Rear->next!=NULL)
            {
                Rear=Rear->next;
            }
            Rear->next=Ptr;
        }
     }
    void CreateGraph(int VerNum,int EdgeNum)
     {//顶点从0开始
         int i;Graph->VerterNum=VerNum;Graph->EdgeNum=EdgeNum;
             for(i=0;i<VerNum;i++)
             {
                 Graph->LG[i].pcnt=NULL;
                 Graph->LG[i].FirstEdge=NULL;
                 memset(Graph->LG[i].prev,-1,sizeof(int)*5);
             }
             int NN,MM,KK,CC;
             for(i=0;i<EdgeNum;i++)
             {
                cin>>NN>>MM>>KK>>CC;
                Insert(NN,MM,KK,CC);
             }
     }
    
    
     int Get_Weight(int Start,int FindNum)
     {
         PtrToAdjVNode *Rear=Graph->LG[Start].FirstEdge;
         int Value=INF;
         if(Start==FindNum)
             return 0;
         while(Rear)
         {
             if(Rear->Adj==FindNum)
                 
                 {   
                            Value=Rear->Weight;
                            break;
                }
             else
             {
                 Rear=Rear->next;
             }
    
         }
         return Value;
     }
     void Search(int prev[],int Start,int end)
     {
        //简单摆Dijkstra算法遍历
         int i=end,j=0;int *rev=new int[Graph->VerterNum];
         //倒序的路径
         for(i=end;i!=Start;i=prev[i])
         {
            cout<<"["<<i<<"] ";
            rev[j++]=i;
         }
        cout<<"["<<i<<"] ";
        rev[j]=Start;
        
        int pathCost=0;
        PtrToAdjVNode *Rear;int K=1;
        while(j>0)
        {
            Rear=Graph->LG[rev[j]].FirstEdge;
            
            if(Rear->next && K)
            {//防止出现双等路径,只标记第一个路径上,这种方式不行的
                //Graph->LG[rev[j]].Flag=true;
                K=0;
    
            }
    
            while(Rear)
            {
                if(rev[j-1]==Rear->Adj)
                {    
                    pathCost+=Rear->CostValue;
                    break;
                }
                else
                {    
                    Rear=Rear->next;
                }
            }
            j=j-1;
        }
        cout<<"最短路径上花费"<<pathCost<<endl;
    
     }
     int Calculate(int A,int B)
     {//计算出两个地图之间花费
         PtrToAdjVNode *Rear=Graph->LG[A].FirstEdge;
         int SiglePath=0;
        while(Rear)
        {
            if(B==Rear->Adj)
                {    
                    SiglePath=Rear->CostValue;
                    break;
                }
                else
                {    
                    Rear=Rear->next;
                }
        }
        return SiglePath;
     }
     void PathCost(int start,int end)
     {
     //多条相等路径,比较相同同时对其进行
         int Trarget=end,Trarget1;
         int *path=new int[Graph->VerterNum];
         int i=0;int cost=0;
         while (Trarget!=start)
         {  
             path[i++]=Trarget;
             if(Graph->LG[Trarget].pcnt==1)
             {
                 
                 Trarget1=Graph->LG[Trarget].prev[0];
                 cost+=Calculate(Graph->LG[Trarget].prev[0],Trarget);
                 Trarget=Trarget1;
             }
             else
             {
                 //从一系列相等的之中挑选出最小花费路径
                // path[i++]=Trarget;
                 int MinINf=INF,Tp;
                 for(int j=0;j<Graph->LG[Trarget].pcnt;j++)
                 {
                     Tp=Calculate(Graph->LG[Trarget].prev[j],Trarget);
                     if(Tp<MinINf)
                     {
                        MinINf=Tp;
                        
                        Trarget1=Graph->LG[Trarget].prev[j];
                     }
                 }
                 cost+=MinINf;
                 Trarget=Trarget1;
             }
            
         }
         path[i]=start;
         cout<<"多条相等路径中花费最小前:"<<cost<<endl;
         for(;i>=0;i--)
         {
         cout<<"["<<path[i]<<"] ";
         }
    
         
     }
     void Dijkstra(int dist1[],int prev1[],int Vstart,int Vend)
     {
        //防止出现两条相等路径,最外面只将从Start---End点上的最短路径上顶点在表头上标记
         //最外面只标记最短路径,最好只标记与顶点相邻的分叉点
         /***第一步计算出S点与相邻点的dist,同时初始化*/
         int prev[10],dist[10],flag[10];
         int i;//int *flag=new int[Graph->VerterNum];
         for(i=0;i<Graph->VerterNum;i++)
         {
            
            flag[i]=0;
            dist[i]=Get_Weight(Vstart,i);//只能够得到他么最短路径
            if(dist[i]<INF)
            {    prev[i]=Vstart;
            Graph->LG[i].prev[Graph->LG[i].pcnt++]=Vstart;
             }
            else
                prev[i]=-1;
         }
         //接下来是Dijkstra 核心,从U集合中选取一个最短路径的点加入S集合
         //同时更新比较经过k 点与不经过K点到k 的相邻点上距离。
    
         prev[Vstart]=Vstart;flag[Vstart]=1;
         int m,n,kIndex,j;
         int MIndex=Vstart;
         int Min,Tmp; int Min1=INF,K1;
    
         for(m=0;m<Graph->VerterNum;m++)//大循环保证每个点都能遍历
         {
             Min=INF;Min1=INF;
            //从U中挑选出离S集合最近的点,通过比较,将各个顶点与S中点相比较
             for(n=0;n<Graph->VerterNum;n++)
             {
                 //从没有遍历的点中找到最短路径的点,使用最小堆的方式,每一次的时候可以从最小堆中抛出dist中最短路径,减少运算
                 
                 if(Graph->LG[n].FirstEdge  && !flag[n] )
                 {
                     if(dist[n]<Min)
                     {
                         Min=dist[n];
                         kIndex=n;
                     }
                 }
             }
             /**上面步骤主要是从相近点中找到最短路径的点,dist存储各个点情况,只有路径就可以到达,保证找到最终点*/
             //如果找到最终点刚好在最短路径上,可以用之下方法
             //只从点的相邻点中找路径,flag表示已经遍历dist存储各个点情况
             PtrToAdjVNode *Rear=Graph->LG[MIndex].FirstEdge; //从相邻点中找最短路径,保证最终点在路径上,但是找出图中最短的点
             while(Rear)
             {//缺点,如果算多条相等路径,无法计算出来,如果想得出多条路径,判断此点相连的最短路径的点是否有多条。static对其进行储存。这样可以得出多条路径
                 if(flag[Rear->Adj]==0)
                 {
                     if(dist[Rear->Adj]<Min1)
                     {
                         Min1=dist[Rear->Adj];
                         MIndex=Rear->Adj;
                     }
                 }
                 Rear=Rear->next;
             }
              flag[kIndex]=1;
            
             //不断更新dist同prev
            for(j=0;j<Graph->VerterNum;j++)
            {
                Tmp=Get_Weight(kIndex,j);
                
                    Tmp=(Tmp==INF)?INF:(Min+Tmp);
                
                    if(!flag[j]  && dist[j]>Tmp)//只有在修改时候才能进行prev修改
                    {    
                        dist[j]=Tmp;
                        prev[j]=kIndex;
                        Graph->LG[j].prev[Graph->LG[j].pcnt++]=kIndex;
                    }
                    else if(dist[j]==Tmp && dist[j]!=INF && !flag[j] && kIndex!=j)
                    {
                        Graph->LG[j].prev[Graph->LG[j].pcnt++]=kIndex;
                        flag[kIndex]=1;
                    }
                }
            
            }
         //对路径的回溯
    
         Search(prev,Vstart,Vend);
         //多条相等的路径存取在头列表中如何对其进行遍历
         PathCost(Vstart,Vend);
    
             }
        
     
     int main()
     {
        int VerNum,EdgeNum,VerStart,Verend;
        cin>>VerNum>>EdgeNum>>VerStart>>Verend;
        CreateGraph(VerNum,EdgeNum);
    
        //选择出最短路径,如果有相同最短路径在计算出最小花费
        int *dist=new int[VerNum]; int *prev=new int[VerNum];
            Dijkstra(dist,prev,VerStart,Verend);
     return 0;
     }

     二.Floyed 算法(o(N^3))在C语言中明显如何发生溢出

    多源路径最短算法:

  • 相关阅读:
    orm 对象关系映射 指 表与类之间的映射 # 40
    事务 视图 触发器 函数 (内置) 存储过程 流程控制 索引 # 39
    exist 存在 Python操作mysql pymysql sql注入问题 # 38
    基本查询语句与方法 多表查询 # 37
    外键 #36
    存储引擎 索引 数据类型 约束条件 # 35
    mysql安装 登录 修改密码 库,表,记录(增删改查) # 34
    进程池和线程池 协程 # 33
    GIL全局解释器锁
    # 并发编程 -进程理论-进程的方法
  • 原文地址:https://www.cnblogs.com/woainifanfan/p/6051482.html
Copyright © 2011-2022 走看看