Dijkstra算法的实现

来源:互联网 发布:网络借贷风险 编辑:程序博客网 时间:2024/05/21 21:33

最近自学了Dijkstra算法,跟着自己的理解写了个代码,想加深对Dijkstra算法的理解,如有不足,还请多多指教。

首先,Dijkstra算法主要是适用于找两点之间的最小权值之和。

思路:
开始的时候,假设有两个集合(分别为A,B集合),A集合为空(用来存放使用过的点),B集合存放着图中的各点(当然,在写代码的时候不一定要用到集合,能表达这个意思就行),先将起始点(假设为点a)放入A集合中,开始运行的时候,依次遍历B集合中的元素,查找A集合中起始点所能到达的在B集合中的点的最短路径,记录下最短的路径,及其所到达的点(假设为点b),之后以b点作为中间点,查找点b所能到达的点(假设为点c,d),之后比较a到b再到c或d的距离与a到c或a到d的距离,若a经过中间点b再到其它点的距离小于a直接到其它点的距离,则更新a到其它点的距离为a到中间点的距离再到其它点的距离(如:a->b=1,b->c=3,a->c=5,则更新完后a->c=4),循环完一次后,将所找到的最小值的点加入到集合A中,之后依次在集合B中重复上述步骤至集合B中的全部元素取出即可。

下面,根据下图对Dijkstra进行分析:
这里写图片描述

我们下面以求两点之间的最短路径为例子:
在上图中,假设A为起点,F为目标点,要求A到F的最短路径。

先来看一下代码框架:
这里写图片描述


1. 首先先定义两个结构体,一个为图,一个保存图中两个点之间的权值,及判断该点是否在集合中。

struct Arc_{    int value;//路径长度    bool is_in_set;//判断该点是否在集合B中,初始时全在集合B中,全初始化为True};struct MGraph{    unsigned vertax;//顶点数    int arcnum;//图的边数    Arc_ arc[maxnum][maxnum];//记录图的的信息};

2. 对图先进行初始化,初始化时输入顶点的个数及边数后,将任意两点间的距离初始化为无穷大,同时将 is_in_set 全部初始化为true,初始化完成后输入数据,给图中指定两点赋值。


3. 对图设置完之后开始Dijkstra算法,先将起始点加入到集合A中(及将起止点的 is_in_set 置为false),开始在B集合中循环,依次找到最小的点,详情见上面的思路,接下来贴代码:

    //----------------begin-----------------//    for (v = 0; v < G->vertax; v++)//第一个循环作为次数控制    {        int min_value = max_int;//保存两点间的最短路径,初始时另路径为最大值        int min_j = v0;//记录最短路径点的坐标        for (x = 0; x < G->vertax; x++)//内层第一个循环找出距离v0最近的点,记录下其最小值及距离最短的点的坐标        {            if (G->arc[v0][x].is_in_set == true)//只选取集合B中的点            if (G->arc[v0][x].value < min_value)            {                min_value = G->arc[v0][x].value;                min_j = x;            }        }        G->arc[v0][min_j].is_in_set = false;//接下来的循环中min_j将被走过,故为false        G->arc[min_j][v0].is_in_set = false;        for (x = 0; x < G->vertax; x++)        {            if (G->arc[v0][x].is_in_set == true && G->arc[min_j][x].value < max_int)//判断是否是min_j所连通的点同时判断v0是否可直接或间接到达x点            if (G->arc[v0][min_j].value + G->arc[min_j][x].value < G->arc[v0][x].value)            {//比较v0间接达到x点的距离和v0直接到达x点的距离,若间接达到更近,则更新v0到x的距离                G->arc[v0][x].value = G->arc[v0][min_j].value + G->arc[min_j][x].value;                G->arc[x][v0].value = G->arc[v0][x].value;            }        }    }//-------------------------end----------------------

最后的运行如下图所示:
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述


最后贴上全代码:

/*name:Dijkstra算法author :bbytime:2017.4.19language:C++*/#include<iostream>#include<Windows.h>using namespace std;const int maxnum = 100;const int max_int = 999999;struct Arc_{    int value;//路径长度    bool is_in_set;//表示起始点到此点是否已走过};struct MGraph{    unsigned vertax;//顶点数    int arcnum;//图的边数    Arc_ arc[maxnum][maxnum];//记录图的的信息};int Dijkstra(MGraph *, int, int);void Init(MGraph *);//对图进行初始化void Input(int&, int&, int&);//对图进行赋值void Print_G(MGraph*, MGraph*);//输出图void Copy_G(MGraph*, MGraph*);//复制图的路径长度bool is_equal(MGraph*, MGraph*);//判断两个图是否相等//测试函数int main(){    MGraph *G = new MGraph;    int v0, v, distance;//v0为起点 v为终点    Init(G);    cout << "请输入起点:";    cin >> v0;    cout << "请输入终点:";    cin >> v;    cout << endl;    distance = Dijkstra(G, v0, v);    cout << endl;    cout << "由起点" << v0 << "到终点" << v << "的最短路径为" << distance << endl << endl;    delete G;    system("pause");    return 0;}//对图进行初始化void Init(MGraph*G){    try{        cout << "请输入顶点数及边数:";        cin >> G->vertax >> G->arcnum;        if (cin.fail() || G->vertax <= 0 || G->arcnum <= 0 || G->arcnum > G->vertax*(G->vertax - 1) / 2)//边数最多为C2n条  n为顶点数  所以边数<=(顶点数-1)*顶点数/2            throw(G->vertax);    }    catch (int)    {        cout << "输入错误,请重新输入" << endl;        do        {            cin.clear();            cin.sync();        } while (!cin.good());        Init(G);    }    for (int i = 0; i < G->vertax; i++)//初始化图全部为无穷    for (int j = 0; j < G->vertax; j++)    {        G->arc[i][j].value = max_int;        if (i == j)            G->arc[i][j].value = 0;        G->arc[i][j].is_in_set = true;    }    int i, j, value;    for (int k = 1; k <= G->arcnum; k++)    {        cout << "输入第" << k << "条边的景点对:(i,j)以及它的路径长度:";        Input(i, j, value);        G->arc[i][j].value = G->arc[j][i].value = value;    }    cout << "初始化完成!" << endl << endl;}//对图进行赋值void Input(int&i, int&j, int&value){    try{        cin >> i >> j >> value;        if (cin.fail() || i < 0 || j < 0 || value < 0)            throw(i);    }    catch (int)    {        cout << "输入错误,请重新输入" << endl;        do        {            cin.clear();            cin.sync();        } while (!cin.good());        Input(i, j, value);    }}//Dijkstra算法int Dijkstra(MGraph *G, int v0, int v1){    unsigned v, x, y, z = 0;//循环变量    G->arc[v0][v0].is_in_set = false;    Sleep(1000);    system("cls");    cout << "-----------初始图如下----------------" << endl;    Print_G(G, G);    Sleep(1000);    cout << endl;    MGraph* Before_G = new MGraph;    //----------------begin-----------------//    for (v = 0; v < G->vertax; v++)//第一个循环作为次数控制    {        Copy_G(G, Before_G);        int min_value = max_int;//保存两点间的最短路径,初始时另路径为最大值        int min_j = v0;//记录最短路径点的坐标        for (x = 0; x < G->vertax; x++)//内层第一个循环找出距离v0最近的点,记录下其最小值及距离最短的点的坐标        {            if (G->arc[v0][x].is_in_set == true)            if (G->arc[v0][x].value < min_value)            {                min_value = G->arc[v0][x].value;                min_j = x;            }        }        G->arc[v0][min_j].is_in_set = false;//接下来的循环中min_j将被走过,故为false        G->arc[min_j][v0].is_in_set = false;        for (x = 0; x < G->vertax; x++)        {            if (G->arc[v0][x].is_in_set == true && G->arc[min_j][x].value < max_int)//判断是否是min_j所连通的点同时判断v0是否可直接或间接到达x点            if (G->arc[v0][min_j].value + G->arc[min_j][x].value < G->arc[v0][x].value)            {//比较v0间接达到x点的距离和v0直接到达x点的距离,若间接达到更近,则更新v0到x的距离                G->arc[v0][x].value = G->arc[v0][min_j].value + G->arc[min_j][x].value;                G->arc[x][v0].value = G->arc[v0][x].value;            }        }        if (!is_equal(G, Before_G)){            system("cls");            cout << "即将改变的数如下图红色所示:" << endl << endl;            Print_G(G, Before_G);            Sleep(3000);            system("cls");            cout << "第" << ++z << "次改变输出如下:" << endl << endl;            Print_G(G, G);            Sleep(3000);            cout << endl;        }    }//-------------------------end----------------------    //释放内存    delete Before_G;    return G->arc[v0][v1].value;}//输出图void Print_G(MGraph*G, MGraph*Before_G){//输出图的时候比较改变后和改变前的数据 输出不同的颜色以便观察    for (int i = 0; i < G->vertax; i++)    {        for (int j = 0; j < G->vertax; j++)        {            if (G->arc[i][j].value == Before_G->arc[i][j].value)            {                if (G->arc[i][j].value < max_int)                {                    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);//当数字为进行更新时则输出默认白色                    cout << G->arc[i][j].value << "\t";                }                else                {                    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);//当路径未连通则输出蓝色                    cout << "∞" << "\t";                }            }            else            {//当数字即将进行改变时 输出红色                if (Before_G->arc[i][j].value < max_int){                    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);                    cout << Before_G->arc[i][j].value << "\t";                }                else{                    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);                    cout << "∞" << "\t";                }            }        }        cout << endl << endl;    }}//复制图的路径长度 void Copy_G(MGraph*G, MGraph*Before_G){    for (int i = 0; i < G->vertax; i++)    for (int j = 0; j < G->vertax; j++)        Before_G->arc[i][j].value = G->arc[i][j].value;}//判断两个图是否相等bool is_equal(MGraph*G, MGraph*Before_G){    for (int i = 0; i < G->vertax;i++)    for (int j = 0; j < G->vertax;j++)    if (G->arc[i][j].value != Before_G->arc[i][j].value)        return false;    return true;}
1 0