Dijkstra算法的实现

最近自学了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 :bby
time:2017.4.19
language: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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值