最短路径求解(Dijkstra)

Dijkstra算法分析

题目分析参照《数据结构》(严蔚敏)7-6节

最短路径问题描述

参照日常生活中的公交查询系统。我们有选项:

少换乘/最少站数

价格最少/时间最短....

(ps:下边这个图是网页查询的,略有出入)

根据这样的分类。我们可以将最短路径分为:结点最少(经过的站数最少),权值最小(这个就是个心里期望了,看你是相花费时间最少,金钱最少....)

结点最少

(参照途中描述)

由此可以看出,对于经过站点最少,换乘最少这种问题,我们只需要对图进行广度遍历,即可获取相关结果。

我们重点分析下面的情况

权值最小(花费最少)

理论:从A到B,他们之间的路径要么是A->B,要么经过中间节点  A->..->B其最短路径也就是两条路径中最短的一条。

于是有:对于最短路径问题,我们只需要利用动态规划,在遍历中更新,逐步获取最短路径。

具体分析图如下

如上为寻找下标0-2结点过程的分析。对应代码

    bool Dijkstra(const V&src,const V&dst,int &ret)
    {
        //如果只有顶点,那么返回true,ret =0;
        if (_size <= 1)
        {
            ret = 0;
            return true;
        }
        int cur = FindIndexV(src);
        int end = FindIndexV(dst);

        int beg = cur;

        size_t wights[6] = {};
        int paths[6] = {};
        for (size_t i = 0; i < _size; ++i)
        {
            wights[i] = -1;
            paths[i] = src;
        }
        wights[cur] = 0;
        paths[cur] = 0;

        Edge* pcur = _eList[cur];
        //首次更新
        while (pcur)
        {
            wights[pcur->_dst] = pcur->_wight;
            pcur = pcur->_next;
        }
        pcur = _eList[cur];

        int visitedCount = 0;
        while (cur!=end)//未走到目的
        {
            if (cur == beg)
                visitedCount++;
            //如果起点没有路径且目标不可达//或者回到起点了
            if (pcur == NULL&&wights[dst] == -1||cur == beg&&visitedCount==2)
            {
                return false;
            }

            //获取最短边
            Edge* minCur = _eList[cur];
            Edge* pcur = _eList[cur];
            while (pcur)
            {
                if (minCur->_wight > pcur->_wight)
                    minCur = pcur;
                pcur = pcur->_next;
            }
            cur = minCur->_src;
            //根据局部最短更新路径
            if (wights[cur] + minCur->_wight < wights[minCur->_dst])
            {
                wights[minCur->_dst] = wights[cur] + minCur->_wight;
                paths[minCur->_dst] = minCur->_src;
            }

            cur = minCur->_dst;
            if (minCur->_dst == FindIndexV(dst))
            {
                ret = wights[minCur->_dst];
                return true;
            }
        }
    }

以下是整个图项目文件以及对应于最短路径的测试用例

#pragma once
//邻接表实现图

#include<queue>
#include<stack>
#include"UnionFindset.h"

#include<map>
template<class V, class E>
struct Edge
{
    Edge(size_t dst,size_t src, const E&e)
        :_wight(e)
        ,_dst(dst)
        ,_src(src)
        , _next(NULL)
    {}
    E _wight;       //权值,边比重
    size_t _dst;    //目的顶点下标
    size_t _src;    //源顶点下标
    struct Edge<V, E>* _next;

    bool operator<(const Edge* &ed)
    {
        return _wight < ed->_wight;
    }
};

template<class V,class E>
class GraphList
{
    typedef Edge<V, E> Edge;
protected:
    V* _vArr;               //顶点存储数组
    size_t _size;

    Edge** _eList;    //边存储指针数组

public:
    GraphList(const V* vArray, const size_t size)
        :_size(size)
        , _vArr(new V[size])
    {
        //初始化顶点保存
        for (size_t i = 0; i < size; ++i)
        {
            _vArr[i] = vArray[i];
        }
        //初始化边结构
        _eList = new Edge*[size];
        memset(_eList, 0, sizeof(Edge*)*size);
    }

    int FindIndexV(const V& v) const
    {
        for (size_t i = 0; i < _size; ++i)
        {
            if (_vArr[i] == v)
                return i;
        }
        return -1;
    }

    //添加v1->v2的边
    void AddEdge2(const V& v1, const V&v2, const E& e, bool IsDir = true)
    {
        int ind1 = FindIndexV(v1);
        int ind2 = FindIndexV(v2);

        Edge* cur = new Edge(ind2, ind1, e);

        cur->_next = _eList[ind1];
        _eList[ind1] = cur;

        if (!IsDir)
        {
            Edge* cur = new Edge(ind1, ind2, e);
            cur->_next = _eList[ind2];
            _eList[ind2] = cur;
        }

    }

    void Display()const
    {
        cout << "顶点集合" << endl;
        for (size_t i = 0; i < _size; ++i)
        {
            cout << _vArr[i] << " ";
        }
        cout << endl << "边表示" << endl;

        for (size_t i = 0; i < _size; ++i)
        {
            cout << "边["<<i << "]>>";
            Edge* cur = _eList[i];
            while (cur)
            {
                //cout << "[" << cur->_dst << "]" << cur->_wight << " ";
                //printf("[%d]:", cur->_dst, cur->_wight);
                cout << "[" << cur->_dst << "]" << cur->_wight << "--> ";
                cur = cur->_next;
            }
            cout <<"NULL"<< endl;
        }
        cout << endl;
    }

    //广度优先
    void BSP(const V& root)
    {
        cout << "广度优先遍历:" << endl;
        bool *visited = new bool[_size]();

        queue<int> q;
        int index = FindIndexV(root);

        q.push(index);

        while (!q.empty())
        {
            index = q.front();
            if (visited[index] == false)
            {
                cout << _vArr[index]<<"-->";
            }

            visited[index] = true;

            q.pop();
            Edge* cur = _eList[index];
            while (cur)
            {
                if (visited[cur->_dst] == false)//未访问过那么压入
                {
                    q.push(cur->_dst);
                }
                cur = cur->_next;
            }
        }
        cout << endl << endl;
    }

    //深度优先
    void DSP(const V& root)
    {
        //
        cout << "深度优先遍历:" << endl;
        _DSP(root);
        cout << endl << endl;
    }
    void _DSP(const V& root)
    {
        static bool *visited = new bool[_size]();
        int index = FindIndexV(root);
        if (visited[index] == false)
        {
            cout << _vArr[index] << "-->";
            visited[index] = true;
        }

        Edge* cur = _eList[index];

        while (cur)
        {
            if (visited[cur->_dst] == false)
                _DSP(_vArr[cur->_dst]);
            cur = cur->_next;
        }
        if (cur == NULL)
            return;
    }

    //在所有边中获取最小权值的边
    int FindMinEdgeIndex(vector<Edge*>&v)
    {
        int min = 0;
        for (size_t i = 1; i < v.size(); ++i)
        {
            if (v[i]->_wight < v[min]->_wight)
                min = i;
        }
        return min;
    }

    bool Kruskal(GraphList<V,E>& minTree)
    {
        vector<Edge*> ve;
        for (size_t i = 0; i < _size; ++i)
        {
            Edge* cur = _eList[i];
            while (cur)
            {
                //只插入有效边
                ve.push_back(cur);
                cur = cur->_next;
            }
        }

        UnionFindSet us(_size);

        while (!ve.empty())
        {
            //找到最小权值边
            int i = FindMinEdgeIndex(ve);
            //并查集插入相关结点
            bool sure = us.Combine(ve[i]->_src, ve[i]->_dst);
            if (sure)   //如果不是连通的,那么加入该边
            {
                minTree.AddEdge2(_vArr[ve[i]->_src], _vArr[ve[i]->_dst], ve[i]->_wight);
            }
            ve.erase(ve.begin()+i);
        }

        return us.IsOnlyOneRoot();
    }

    //在相关边中获取最小权值的边
    int FindMinEdgeIndexByInGraph(vector<Edge*>&v,vector<int>& nodes)
    {
        if (nodes.size() == 0)
            return FindMinEdgeIndex(v);
        int min = -1;
        for (size_t i = 0; i < v.size(); ++i)   //遍历所有结点
        {
            //如果

            if (v[i]->_wight < v[min]->_wight)
            {
                bool inNodes = false;
                for (size_t j = 0; j < nodes.size(); ++i)
                {
                    if (v[i]->_dst == nodes[j] || v[i]->_src == nodes[j])
                    {
                        inNodes = true;
                        break;
                    }
                }
                if(inNodes)
                    min = i;
            }      

        }
        return min;
    }
    bool Prim(GraphList<V, E>& minTree)
    {
        vector<Edge*> ve;
        vector<int> inGraph;
        for (size_t i = 0; i < _size; ++i)
        {
            Edge* cur = _eList[i];
            while (cur)
            {
                //只插入有效边
                ve.push_back(cur);
                cur = cur->_next;
            }
        }

        UnionFindSet us(_size);

        while (!ve.empty())
        {
            //找到最小权值边
            int i = FindMinEdgeIndexByInGraph(ve,inGraph);
            if (us.IsOnlyOneRoot())
                return true;

            else if (i == -1 && !us.IsOnlyOneRoot())
                return false;

            //并查集插入相关结点
            bool sure = us.Combine(ve[i]->_src, ve[i]->_dst);
            if (sure)   //如果不是连通的,那么加入该边
            {
                minTree.AddEdge2(_vArr[ve[i]->_src], _vArr[ve[i]->_dst], ve[i]->_wight);
            }
            ve.erase(ve.begin() + i);
        }

        return us.IsOnlyOneRoot();
    }
    //size_t wights[6] = {};
    //int paths[6] = {};
    bool Dijkstra(const V&src,const V&dst,int &ret)
    {
        //如果只有顶点,那么返回true,ret =0;
        if (_size <= 1)
        {
            ret = 0;
            return true;
        }
        int cur = FindIndexV(src);
        int end = FindIndexV(dst);

        int beg = cur;

        size_t wights[6] = {};
        int paths[6] = {};
        for (size_t i = 0; i < _size; ++i)
        {
            wights[i] = -1;
            paths[i] = src;
        }
        wights[cur] = 0;
        paths[cur] = 0;

        Edge* pcur = _eList[cur];
        //首次更新
        while (pcur)
        {
            wights[pcur->_dst] = pcur->_wight;
            pcur = pcur->_next;
        }
        pcur = _eList[cur];

        int visitedCount = 0;
        while (cur!=end)//未走到目的
        {
            if (cur == beg)
                visitedCount++;
            //如果起点没有路径且目标不可达//或者回到起点了
            if (pcur == NULL&&wights[dst] == -1||cur == beg&&visitedCount==2)
            {
                return false;
            }

            //获取最短边
            Edge* minCur = _eList[cur];
            Edge* pcur = _eList[cur];
            while (pcur)
            {
                if (minCur->_wight > pcur->_wight)
                    minCur = pcur;
                pcur = pcur->_next;
            }
            cur = minCur->_src;
            //根据局部最短更新路径
            if (wights[cur] + minCur->_wight < wights[minCur->_dst])
            {
                wights[minCur->_dst] = wights[cur] + minCur->_wight;
                paths[minCur->_dst] = minCur->_src;
            }

            cur = minCur->_dst;
            if (minCur->_dst == FindIndexV(dst))
            {
                ret = wights[minCur->_dst];
                return true;
            }
        }
    }

    ~GraphList()
    {
        if (_vArr)
        {
            delete[]_vArr;
            _vArr = NULL;
        }
        if (_eList)
        {
            for (size_t i = 0; i < _size;++i)
            {
                while (_eList[i] != NULL)
                {
                    Edge* del = _eList[i];
                    _eList[i] = del->_next;
                    delete del;
                    del = NULL;
                }
            }
        }
    }
};

void testD()
{
    //int vArr1[] = { 1,2,3,4,5,6,7,8,9 };
    //GraphList<int, int> gh1(vArr1, sizeof(vArr1) / sizeof(vArr1[0]));

    //gh1.AddEdge2(1, 2, 11);
    //gh1.AddEdge2(1, 3, 33);
    //gh1.AddEdge2(1, 5, 33);
    //gh1.AddEdge2(2, 3, 33);
    //gh1.AddEdge2(2, 6, 99);
    //gh1.AddEdge2(5, 3, 33);
    //gh1.AddEdge2(3, 4, 44);
    //gh1.AddEdge2(4, 5, 55);
    //gh1.AddEdge2(4, 7, 32);
    //gh1.AddEdge2(7, 8, 65);
    //gh1.AddEdge2(1, 9, 12);
    //gh1.AddEdge2(9, 7, 22);    

    int vArr1[] = { 0,1,2,3,4,5};
    GraphList<int, int> gh1(vArr1, sizeof(vArr1) / sizeof(vArr1[0]));

    gh1.AddEdge2(0, 3, 10);
    gh1.AddEdge2(0, 2, 50);
    gh1.AddEdge2(3, 1, 20);
    gh1.AddEdge2(1, 2, 10);
    gh1.AddEdge2(2, 4, 40);
    gh1.AddEdge2(4, 0, 20);
    gh1.AddEdge2(4, 1, 30);
    gh1.AddEdge2(5, 1, 10);

    gh1.Display();

    gh1.BSP(1);
    gh1.DSP(1);
    GraphList<int, int> gMin(vArr1, sizeof(vArr1) / sizeof(vArr1[0]));
    GraphList<int, int> gMin1(vArr1, sizeof(vArr1) / sizeof(vArr1[0]));
    if (gh1.Kruskal(gMin))
    {
        cout << "kruskal最小生成树:" << endl;
        gMin.Display();
    }
    if (gh1.Prim(gMin1))
    {
        cout << "prim最小生成树:" << endl;
        gMin1.Display();
    }

    int ret = 0;
    if (gh1.Dijkstra(0, 1, ret))
    {
        cout <<"gh1.Dijkstra(0, 1, ret)"<< ret << endl;
    }
    if (gh1.Dijkstra(0, 2, ret))
    {
        cout << "gh1.Dijkstra(0, 2, ret)" << ret << endl;
    }
    if (gh1.Dijkstra(0, 3, ret))
    {
        cout << "gh1.Dijkstra(0, 3, ret)" << ret << endl;
    }
    if (gh1.Dijkstra(0, 4, ret))
    {
        cout << "gh1.Dijkstra(0, 4, ret)" << ret << endl;
    }
    if (gh1.Dijkstra(0, 5, ret))
    {
        cout << "gh1.Dijkstra(0, 5, ret)" << ret << endl;
    }
    //char vArr2[] = { ‘A‘,‘B‘,‘C‘,‘D‘,‘E‘,‘F‘ };
    //GraphList<char, int> gh(vArr2, sizeof(vArr2) / sizeof(vArr2[0]));
    //gh.AddEdge2(‘A‘, ‘B‘, 11);
    //gh.AddEdge2(‘B‘, ‘C‘, 33);
    //gh.AddEdge2(‘C‘, ‘D‘, 44);
    //gh.AddEdge2(‘D‘, ‘E‘, 55);
    //gh.AddEdge2(‘E‘,‘F‘, 66);
    //gh.Display();

 //   gh.BSP(‘A‘);
}

参考:http://www.cnblogs.com/hxsyl/archive/2013/08/20/3270401.html

时间: 2024-10-09 23:24:59

最短路径求解(Dijkstra)的相关文章

最短路径算法-Dijkstra算法的应用之单词转换(词梯问题)

一,问题描述 在英文单词表中,有一些单词非常相似,它们可以通过只变换一个字符而得到另一个单词.比如:hive-->five:wine-->line:line-->nine:nine-->mine..... 那么,就存在这样一个问题:给定一个单词作为起始单词(相当于图的源点),给定另一个单词作为终点,求从起点单词经过的最少变换(每次变换只会变换一个字符),变成终点单词. 这个问题,其实就是最短路径问题. 由于最短路径问题中,求解源点到终点的最短路径与求解源点到图中所有顶点的最短路径复

单源最短路径(Dijkstra)——贪心算法

Dijkstra算法是解单源最短路径问题的贪心算法.其基本思想是,设置顶点集合点集合S并不断地做贪心选择来扩充这个集合.一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知.初始时,S中仅含有源.设u是G的其一顶点.把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组Distance记录当前每个顶点所对应的最短特殊路径长度.Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶占,Distance就记录了从源到所有其它顶点之间最短路径长度. 例如下图中的有向图,应用Dij

4003.基于Dijsktra算法的最短路径求解

基于Dijsktra算法的最短路径求解 发布时间: 2018年11月26日 10:14   时间限制: 1000ms   内存限制: 128M 有趣的最短路...火候欠佳,目前还很难快速盲打出来,需继续练习.唉....我这暑假都干了些啥orz.(马上考四级,练习一下我蹩脚的英语,各位莫笑侬) 描述 一张地图包括n个城市,假设城市间有m条路径(有向图),每条路径的长度已知.给定地图的一个起点城市和终点城市,利用Dijsktra算法求出起点到终点之间的最短路径. 输入 多组数据,每组数据有m+3行.

HDU 2544:最短路( 最短路径入门 &amp;&amp;Dijkstra &amp;&amp; floyd )

最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 30972    Accepted Submission(s): 13345 Problem Description 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt.但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找

最短路径之Dijkstra算法

Dijkstra算法: 首先,引进一个辅助向量D,它的每个分量D[i]表示当前所找到的从始点v到每个终点vi的的长度:如D[3]=2表示从始点v到终点3的路径相对最小长度为2.这里强调相对就是说在算法过程中D的值是在不断逼近最终结果但在过程中不一定就等于长度.它的初始状态为:若从v到vi有弧,则D为弧上的权值:否则置D为∞.显然,长度为 D[j]=Min{D | vi∈V} 的路径就是从v出发的长度最短的一条.此路径为(v,vj). 那么,下一条长度次短的是哪一条呢?假设该次短路径的终点是vk,

最短路径问题-Dijkstra

概述 与前面说的Floyd算法相比,Dijkstra算法只能求得图中特定顶点到其余所有顶点的最短路径长度,即单源最短路径问题. 算法思路 1.初始化,集合K中加入顶点v,顶点v到其自身的最短距离为0,到其它所有顶点为无穷. 2.遍历与集合K中结点直接相邻的边(U,V,C),其中U属于集合K,V不属于集合K,计算由结点v出发,按照已经得到的最短路径到达U,再由U经过该边达到V时的路径长度.比较所有与集合K中结点直接相邻的非集合K结点该路径长度,其中路径长度最小的顶点被确定为下一个最短路径确定的结点

单源最短路径算法---Dijkstra

Dijkstra算法树解决有向图G=(V,E)上带权的单源最短路径问题,但是要求所有边的权值非负. 解题思路: V表示有向图的所有顶点集合,S表示那么一些顶点结合,从源点s到该集合中的顶点的最终最短路径的权值(程序中用dist[i]表示)已经确定.算法反复选择具有最短路径估计的顶点u 属于 V-S(即未确定最短路径的点,程序中finish[i]=false的点),并将u加入到S中(用finish[i]=true表示),最后对u的所有输出边进行松弛. 程序实现:      输入数据: 5 7 0

有向有权图的最短路径算法--Dijkstra算法

Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijkstra算法是很有代表性的最短路径算法, 在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等.注意该算法要求图中不存在负权边. 问题描述:在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短路径.(单源最短路径) 2.算

图的单源最短路径:Dijkstra算法实现

本文介绍的是图的非负权值的单源最短路径问题.问题的提出是,对于有权图D,t提供源点v,要找到从v到其他所有点的最短路径,即单源最短路径问题,在本文中,解决这一问题,是普遍比较熟悉的Dijkstra算法. 算法核心思想参见维基.简而言之,设集合S存放已经求出了最短路径的点.初始状态S中只有一个点v0,之后每求得v0到vn的最短路径,就会更新v0到所有vn邻接的点的一致的最短路径(不一定是最终的最短路径),如此重复,每次会确定v0到一个点的最短路径,确定好的点加入S中,直至所有点进入S结束.在本文中