<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

body, table{font-family: 微软雅黑; font-size: 13.5pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

迪杰克斯拉(Dijkstra)算法:
  按路径长度递增的次序产生最短路径的算法。算法解决了从某个源点到其余各顶点的最短路径问题。求两个顶点之间的最短路径,整个过程都是一步步求起始顶点到相邻顶点的最短路径,直到到达终点。过程中都是基于已经求出的最短路径的基础之上。

V0→V1    1
V0→V1→V2    4
V0→V1→V2→V4    5
V0→V1→V2→V4→V3    7
V0→V1→V2→V4→V3→V6    10
V0→V1→V2→V4→V3→V6→V7    12
V0→V1→V2→V4→V3→V6→V7→V8    16

在求最短路径的过程中,V0到图中所有路径的最短距离都计算过了一遍,只要保存中间结果就可以在求V0→V8的最短路径的同时得到V0到图中任意点的距离


isInShortestPath数组标记对应下标顶点是不是已经计算在最短路径内了

pathPre数组标记对应下标顶点在最短路径的前驱结点是谁,这个结点和weight数组对应,如下表算法开始
weight[3]=∞,这个距离是(0,3)得到的,所以pathPre[3]=0

weight数组标记当前最短路径到对应下标顶点的最短路径,∞就是到不了了

//算法开始,选中V0顶点开始遍历找最短路径

下标 0 1 2 3 4 5 6 7 8
isInShortestPath 1 0 0 0 0 0 0 0 0

下标 0 1 2 3 4 5 6 7 8
pathPre 0 0 0 0 0 0 0 0 0

下标 0 1 2 3 4 5 6 7 8
weight 0 1 5


//第一次遍历找到(0,1),加入顶点1,1可以到达未加入最短路径的顶点2,3,4; 更新数组

下标 0 1 2 3 4 5 6 7 8
isInShortestPath 1 1 0 0 0 0 0 0 0

下标 0 1 2 3 4 5 6 7 8
pathPre 0 0 1 1 1 0 0 0 0

//V1->V2 = 4 < 5,更新 依次类推更新weight[3] weight[4]

下标 0 1 2 3 4 5 6 7 8
weight 0 1 5 4 8 6



//下一次遍历就会选中V2,(1,2),加入顶点2

下标 0 1 2 3 4 5 6 7 8
isInShortestPath 1 1 1 0 0 0 0 0 0

下标 0 1 2 3 4 5 6 7 8
pathPre 0 0 1 1 2 2 0 0 0

weight[2]+(2,4)=5

 下标 0 1 2 3 4 5 6 7 8
weight 0 1 4 8 6 5 11
//加入weight最小的,且没有加入到最短路径中的顶点4(2,4)

下标 0 1 2 3 4 5 6 7 8
isInShortestPath 1 1 1 0 1 0 0 0 0

小标 0 1 2 3 4 5 6 7 8
pathPre 0 0 1 4 2 4 4 4 0

weight[4] = 5,能到达3,5,6,7

下标 0 1 2 3 4 5 6 7 8
weight 0 1 4 7 5 11 8 11 14


//没加入的3,5,6,7,8中选一条最小权值的边,选择顶点3,(4,3)

下标 0 1 2 3 4 5 6 7 8
isInShortestPath 1 1 1 1 1 0 0 0 0

下标 0 1 2 3 4 5 6 7 8
pathPre 0 0 1 4 2 4 3 4 0

weight[3] = 7,3只能到没到过的6,7+(3,6)=10,weight[6]=11,更新

下标 0 1 2 3 4 5 6 7 8
weight 0 1 4 7 5 8 11 10 14

//5,6,7,8中选一个weight最小的5

下标 0 1 2 3 4 5 6 7 8
isInShortestPath 1 1 1 1 1 1 0 0 0

下标 0 1 2 3 4 5 6 7 8
pathPre 0 0 1 4 2 4 3 5 0

weight[5]=8,5能到为加入最短路径的7 ; weight[5]+(5,7)=13<weight[7],更新

下标 0 1 2 3 4 5 6 7 8
weight 0 1 4 7 5 8 10 14 13

//没加入的6,7,8中选一个最小的6

下标 0 1 2 3 4 5 6 7 8
isInShortestPath 1 1 1 1 1 1 1 0 0

下标 0 1 2 3 4 5 6 7 8
pathPre 0 0 1 4 2 4 3 6 6

weight[6]=10,(6,7)=2,(6,8)=7;  

下标 0 1 2 3 4 5 6 7 8
weight 0 1 4 7 5 8 10 13 12 17
//没加入的7,8中选一个最小的7, weight[7]=12

下标 0 1 2 3 4 5 6 7 8
isInShortestPath 1 1 1 1 1 1 1 1 0

下标 0 1 2 3 4 5 6 7 8
pathPre 0 0 1 4 2 4 3 6 7

weight[7]=12,(7,8)=4

下标 0 1 2 3 4 5 6 7 8
weight 0 1 4 7 5 8 10 12 17 16
//加入最后一个结点8

下标 0 1 2 3 4 5 6 7 8
isInShortestPath 1 1 1 1 1 1 1 1 1
在整个图的遍历搜寻中,从V0顶点到每一个其他顶点的最短距离都在这个过程中保存在weight数组中。
要找到两个点之间的最短路径,只要通过pathPre数组找前驱就好了

/*  Dijksta.cpp /
#include"Dijkstra.h"
namespace meihao
{
        void ShortestPath_Dijkstra(const meihao::Graph& g,int pathPre,weight_vaule_type* weight,int vertexCnt)
        {
                if(nullptr==pathPre||nullptr==weight||0==vertexCnt)
                        return ;
                //定义一个数组标记每个结点是否已经在最短路径中
                int* isInShortestPath = new intvertexCnt;  //初始为0,表示都还没有在最短路径之中
                //默认从图中第0个结点开始,初始化weight,表示0到其他结点的最短路径
                for(int idx=0;idx!=vertexCnt;++idx)
                {
                        weight[idx] = g.getGraphEdgeWeight(0,idx);   //weight[idx]存放的就是0到idx的权值
                        pathPre[idx] = 0;  //weight最开始用0到对应下标的距离来初始化,所以pathPre只能全部是0了
                }
                //计算V0到其他结点的最短路径
                isInShortestPath[0] = 1;
                for(int idx=1;idx!=vertexCnt;++idx)  //V0已经在路径中,只要从下一个顶点开始
                {
                        int min = max_weight_value;
                        int nextShortestPathVertex = 0;  //下一个可以加到最短路径上的顶点
                        //开始遍历weight数组找到一条从V0顶点到对应数组小标顶点的最有最小权值的边,并记录边的另一端顶点
                        for(int iidx=0;iidx!=vertexCnt;++iidx)
                        {
                                if(0==isInShortestPath[iidx]&&
                                        weight[iidx]<min)
                                {
                                        nextShortestPathVertex = iidx;
                                        min = weight[iidx];
                                }
                        }
                        //找到了个可以加入到最短路径的顶点
                        isInShortestPath[nextShortestPathVertex] = 1;  //修改nextShortestPathVertex为1
                        //得到了一个点,通过这个点可以到一些其他顶点,这是后可能路径又会有变化
                        //加入nextShortestPathVertex,更新weight数组
                        for(int iiidx=0;iiidx!=vertexCnt;++iiidx)
                        {
                                /if(0==isInShortestPath[iiidx]&&
                                        (weight[nextShortestPathVertex]+g.getGraphEdgeWeight(nextShortestPathVertex,iiidx))<weight[iiidx])  /  
                                //这里错误的原因是有的weigth是weight_vaule_type能表示的最大值,再加就溢出了
                                if(0==isInShortestPath[iiidx] &&
                                         g.getGraphEdgeWeight(nextShortestPathVertex,iiidx) !=max_weight_value &&     //多一个条件,防止溢出,也就是(nextShortestPathVertex,iiidx)之间有边的顶点
                                         (weight[nextShortestPathVertex]+g.getGraphEdgeWeight(nextShortestPathVertex,iiidx) ) < weight[iiidx] )
                                //如果新加入最短路径的点到其他没有标记到最短路径中的点idx的权值小于之前某点到idx的权值,就可以更新这个权值
                                {
                                        weight[iiidx] = weight[nextShortestPathVertex]+g.getGraphEdgeWeight(nextShortestPathVertex,iiidx);
                                        pathPre[iiidx] = nextShortestPathVertex;
                                }
                        }
                }
        }
        void printShortestPath(int vi,int vj,int* pathPre,int vertexCnt)
        {
                if(nullptr==pathPre||0==vertexCnt||vi<0||vj<0||vi>vj||vi>=vertexCnt||vj>=vertexCnt)
                        return ;
                if(vi==vj)
                {
                        cout<<vi<<" ";
                        return ;
                }
                printShortestPath(vi,pathPre[vj],pathPre,vertexCnt);
                cout<<vj<<" ";
        }
};

/* Dijkstra.h /
#ifndef DIJKSTRA_H
#define DIJKSTRA_H
#include"Graph.h"
namespace meihao
{
        void ShortestPath_Dijkstra(const meihao::Graph& g,int pathPre,weight_vaule_type* weight,int vertexCnt);  //参数pathPre数组是用来存放计算得到到图中某一点的最短路径前驱
        //pathPre[2] = 1,表示V2到V1的最短路径中,V2前面一个点是V1, ...V1->V2...
        //weight是存放指定的一个开始遍历的到对应数组小标的点的权值。eg:图中起始的点V0,weight[2] = N,表示V0到V2的最短路径权值和为N
        //定义一个函数输出指定两点之间的最短路径和权值和
        void printShortestPath(int vi,int vj,int* pathPre,int vertexCnt);
};
#endif


/* testmain.cpp /
#include"Graph.h"
#include<iostream>
#include"Dijkstra.h"
using namespace std;
int main()
{
        meihao::Graph g("data.txt");
        int vertexCnt = g.getGraphVertexNumber();
        int pathPre = new intvertexCnt;
        weight_vaule_type* weight = new weight_vaule_typevertexCnt;
        meihao::ShortestPath_Dijkstra(g,pathPre,weight,vertexCnt);
        for(int idx=0;idx!=vertexCnt;++idx)
        {
                meihao::printShortestPath(0,idx,pathPre,vertexCnt);
                cout<<"路径权值:"<<weight[idx];
                cout<<endl;
        }
        cout<<endl;
        cout<<"图的起始顶点0到终点8的最短路径:";
        meihao::printShortestPath(0,8,pathPre,vertexCnt);
        cout<<"  路径权值:"<<weight[8]<<endl;
        delete []pathPre;
        delete []weight;
        system("pause");
}



/* data.txt /

9
0 1 2 3 4 5 6 7 8
0 1 5 -1 -1 -1 -1 -1 -1
1 0 3 7 5 -1 -1 -1 -1
5 3 0 -1 1 7 -1 -1 -1
-1 7 -1 0 2 -1 3 -1 -1
-1 5 1 2 0 3 6 9 -1
-1 -1 7 -1 3 0 -1 5 -1
-1 -1 -1 3 6 -1 0 2 7
-1 -1 -1 -1 9 5 2 0 4
-1 -1 -1 -1 -1 -1 7 4 0 

弗洛伊德(Floyd)算法:
  Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法。

图中D-1矩阵是存放的图的临界矩阵,对应的P-1矩阵是存放对应顶点的前驱顶点矩阵。
eg:p[V1][V0]=0,(V1,V0),只能是0;  p[V1][V1]=1,(V1,V1); p[V1][V2]=2,(V1,V2)  这是


初始,现以V0为中间顶点更新两个矩阵:
(V1,V0)不变,(V1,V1)不变,(V1,V2)->V1,V0,V2 = 3,之前为5,更新
所以更新两个矩阵,p[V1][V2]=0,表示(V1,V2)最短路径中V2前一个结点是V0,对应的D[V1][V2]=3。这时候的矩阵为P0,D0。

//以V0为中间顶点,所有点(Vi,Vj)经过V0求路径,确定是否要更新
//和初始矩阵一样,没有变化
//以V1为中间顶点,所有点(Vi,Vj)经过V1求路径,确定是否要更新
//之后的每次更新都是在前面得到的中间结果之上,最终到V8,这时候所有点之间的最短路径就都得到了
p[V8][V8]=8
//最终结果,D矩阵中(Vi,Vj)就是Vi,Vj之间的最短路径
//P矩阵中,P[V0][V8]=1表示V0->V8要先经过V0->V1->...->V8,P[V1][V8]=2 ... P[V8][V8]=8

/ Floyd.h */

#ifndef FLOYD_H
#define FLOYD_H
#include"Graph.h"
#include<iostream>
namespace meihao
{
        void ShortestPath_Floyd(const meihao::Graph& g,weight_vaule_type weight,int pathPre);
        //weight相当于D数组,pathPre相当于P数组
        void printPath(int vi,int vj,int vertexCnt,int** pathPre);
        //打印vi到vj的最短路径
};
#endif


/* testmain.cpp */

#include"Graph.h"
#include<iostream>
#include"Floyd.h"
#include<iomanip>
using namespace std;
int main()
{
        cout<<"test Floyd:"<<endl;
        meihao::Graph g("data.txt");
        int vertexCnt = g.getGraphVertexNumber();
        int** pathPre = new int*vertexCnt;
        for(int idx=0;idx!=vertexCnt;++idx)
                pathPre[idx] = new intvertexCnt;
        weight_vaule_type** weight = new weight_vaule_typevertexCnt;
        for(int idx=0;idx!=vertexCnt;++idx)
                weight[idx] = new weight_vaule_type[vertexCnt];
        meihao::ShortestPath_Floyd(g,weight,pathPre);
        cout<<"print weight matrix:"<<endl;
        for(int idx=0;idx!=vertexCnt;++idx)
        {
                for(int iidx=0;iidx!=vertexCnt;++iidx)
                {
                        cout<<setw(3)<<weight[idx][iidx]<<" ";
                }
                cout<<endl;
        }
        cout<<endl;
        cout<<"print pathPre matrix:"<<endl;
        for(int idx=0;idx!=vertexCnt;++idx)
        {
                for(int iidx=0;iidx!=vertexCnt;++iidx)
                {
                        cout<<setw(3)<<pathPre[idx][iidx]<<" ";
                }
                cout<<endl;
        }
        cout<<endl;
        for(int idx=0;idx!=vertexCnt;++idx)
        {
                meihao::printPath(0,idx,vertexCnt,pathPre);
                cout<<"路径权值: "<<weight[0][idx]<<endl;
        }
        //释放动态内存
        for(int idx=0;idx!=vertexCnt;++idx)
        {
                delete []pathPre[idx];
                pathPre[idx] = nullptr;
                delete []weight[idx];
                weight[idx] = nullptr;
        }
        delete []pathPre;
        pathPre = nullptr;
        delete []weight;
        weight = nullptr;
        system("pause");
}

/ Floyd.cpp */

#include"Floyd.h"
#include<iostream>
namespace meihao
{
        void ShortestPath_Floyd(const meihao::Graph& g,weight_vaule_type weight,int pathPre)
        {
                if(nullptr==weight||nullptr==pathPre)
                        return ;
                //读取图中数组初始化weight和pathPre
                int vertexCnt = g.getGraphVertexNumber();
                for(int idx=0;idx!=vertexCnt;++idx)
                {
                        for(int iidx=0;iidx!=vertexCnt;++iidx)
                        {
                                weight[idx][iidx] = g.getGraphEdgeWeight(idx,iidx);
                                pathPre[idx][iidx] = iidx;
                        }
                }
                //开始依次选择中间结点,更新weight和pathPre数组
                //中间结点为0时和初始化的矩阵一样
                for(int idx=1;idx!=vertexCnt;++idx)
                {
                        for(int v=0;v!=vertexCnt;++v)
                        {
                                for(int w=0;w!=vertexCnt;++w)
                                {
                                        if( (max_weight_value!=weight[v][idx] && max_weight_value!=weight[idx][w]) &&  //经过中间结点idx,(v,idx)和(idx,w)都不能是最大值,不然下边的相加判断会溢出
                                                //max_weight_value是权值weight_vaule_type所能表示的最大值了,再加就溢出
                                                 weight[v][w]>(weight[v][idx] + weight[idx][w]) )
                                        {//更新
                                                weight[v][w] = weight[v][idx] + weight[idx][w];
                                                pathPre[v][w] = pathPre[v][idx];  //pathPre[v][w] = idx,也就是(v,w)要先到达idx,但是(v,idx)不是一步就到达的,也要先经过pathPre[v][idx]
                                                //pathPre[v][w] = idx;  //不能写这个,要在前面得到的中间结果的基础之上
                                        }
                                }
                        }
                }
        }
        void printPath(int vi,int vj,int vertexCnt,int** pathPre)
        {
                if(vi<0||vj<0||vertexCnt<=0||nullptr==pathPre)
                        return ;
                if(vi==vj)
                {
                        cout<<vi<<" ";
                        return ;
                }       
                int max = vi>=vj?vi:vj;
                int min = vi<vj?vi:vj;
                cout<<min<<" ";  //从最小的起始点开始
                while(min!=max)
                {
                        cout<<pathPre[min][max]<<" ";  //第一个要经过的点
                        min = pathPre[min][max];  //更新起始顶点
                }
        }
};

最短路径:Dijkstra & Floyd 算法图解,c++描述的更多相关文章

  1. 最短路径---Dijkstra/Floyd算法

    1.Dijkstra算法基础: 算法过程比prim算法稍微多一点步骤,但思想确实巧妙也是贪心,目的是求某个源点到目的点的最短距离,总的来说dijkstra也就是求某个源点到目的点的最短路,求解的过程也 ...

  2. 最短路径-Dijkstra+Floyd+Spfa

    Dijkstra算法: Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijkstra ...

  3. 数据结构与算法--最短路径之Floyd算法

    数据结构与算法--最短路径之Floyd算法 我们知道Dijkstra算法只能解决单源最短路径问题,且要求边上的权重都是非负的.有没有办法解决任意起点到任意顶点的最短路径问题呢?如果用Dijkstra算 ...

  4. 【最短路径】 常用算法图解+1376:信使(msner)六解

    进入图之后,最短路径可谓就是一大重点,最短路径的求法有很多种,每种算法各有各的好处,你会几种呢?下面来逐个讲解. 1 floyed算法 1)明确思想及功效:在图中求最短路还是要分开说的,分别是单源最短 ...

  5. 最短路径问题——floyd算法

    floyd算法和之前讲的bellman算法.dijkstra算法最大的不同在于它所处理的终于不再是单源问题了,floyd可以解决任何点到点之间的最短路径问题,个人觉得floyd是最简单最好用的一种算法 ...

  6. 最短路径 - 弗洛伊德(Floyd)算法

    为了能讲明白弗洛伊德(Floyd)算法的主要思想,我们先来看最简单的案例.图7-7-12的左图是一个简单的3个顶点的连通网图. 我们先定义两个二维数组D[3][3]和P[3][3], D代表顶点与顶点 ...

  7. 图的最短路径---弗洛伊德(Floyd)算法浅析

    算法介绍 和Dijkstra算法一样,Floyd算法也是为了解决寻找给定的加权图中顶点间最短路径的算法.不同的是,Floyd可以用来解决"多源最短路径"的问题. 算法思路 算法需要 ...

  8. 每一对顶点间最短路径的Floyd算法

    Floyd思想可用下式描述: A-1[i][j]=gm[i][j] A(k+1)[i][j]=min{Ak[i][j],Ak[i][k+1]+Ak[K+1][j]}    -1<=k<=n ...

  9. 图论-最短路径<Dijkstra,Floyd>

    昨天: 图论-概念与记录图的方法 以上是昨天的Blog,有需要者请先阅读完以上再阅读今天的Blog. 可能今天的有点乱,好好理理,认真看完相信你会懂得 分割线 第二天 引子:昨天我们简单讲了讲图的概念 ...

随机推荐

  1. 安装xmlspy之后,链接及邮箱等都用这个软件打开,怎样取消?

    安装xmlspy之后,链接及邮箱等都用这个软件打开,怎样取消? 安装xmlspy之后,大部分的链接就会用这个软件打开,比较糟心.所以尝试很多的方法,终于解决了. (1)打开控制面板,找到默认程序: ( ...

  2. python中的静态方法、类方法、属性方法(福利:关于几种方法更好的解释)

    该部分的三个属性都是高级方法,平时用的地方不是很多 一.静态方法 静态方法的使用不是很多,可以理解的就看一下,用的地方不是很多 class Dog(object): def __init__(self ...

  3. Determine destination location of apt-get install <package>?

    https://askubuntu.com/questions/129022/determine-destination-location-of-apt-get-install-package dpk ...

  4. OpenVPN参数详解

    一般选项: –config file : 从file中读取配置选项. –help : 显示选项. –version : 显示版权和版本信息. 隧道选项: –local host : 本地主机名或IP地 ...

  5. p1467 Runaround Numbers

    直接搜就行. #include <iostream> #include <cstdio> #include <cmath> #include <algorit ...

  6. centos6 安装python3.5后pip无法使用的处理

    现象:安装pip后发现命令无法识别command not found 原因:which查看找到不到执行路径   find搜索发现安装后存放在/usr/local/python3.5/bin下,于是判断 ...

  7. Confluence 6 配置边栏链接

    选择图标来显示或者隐藏,页面(pages),博客页面(blogs),快捷键(shortcuts )或者导航选项(navigation options).例如,如果你希望i的这个公开主要用于博客用途,你 ...

  8. MySQL事务(三)

    一.事务(Innodb锁)的隔离级别概述 并发事务带来的问题: 更新丢失(lost update):当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会 ...

  9. 2.2 UML用例模型

    参与者(Actor) 参与者(注:有另一种翻译“执行者”) 代表位于系统之外并和系统进行交互的一类事物(人.物.其他软件子系统等) 通过它,可以对软件系统与外界发生的交互进行分析和描述 通过它,可以了 ...

  10. mysql find_in_set()函数的使用

    mysql 中 find_in_set() 函数语法: FIND_IN_SET(str,strList) str 要查询的字符串 strList 字段名,参数以“,”分隔,如(1,2,6,8) 查询字 ...