Dijkstra算法

========================================================================

       这主要是用来找起点与终点最短路径的方法,搜索是以目标节点全遍历的方式进行搜索,一步步确定每个节点的最短路径才终止.

       操作过程中分两集合:  确定最短路径顶点集合U 与 未确定最短路径顶点集合V.  利用U里面的有向向量链分别搜索V里面的每个节点,形成的最短链对应的那个节点就可以确定最短路径了,其就可以加入集合U,逐步搜索整个集合V,直到V中数量减小到零,全部确定.

======================================================================== 

   先上基本搜索步骤图对算法理解.转自:图之Dijkstra算法   

   图之Dijkstra算法

     Dijkstra算法是一种求单源最短路算法,即从一个点开始到所有其他点的最短路。其步骤如下: (原文链接有参考代码.)


1

2
 

3

4
 

5

6
 


    既然理解了搜索步骤,那我们看看其中的思想.  参见: 最短路径之Dijkstra算法详细讲解

最短路径之Dijkstra算法详细讲解

1  最短路径算法

在日常生活中,我们如果需要常常往返A地区和B地区之间,我们最希望知道的可能是从A地区到B地区间的众多路径中,那一条路径的路途最短。最短路径问题是图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。 算法具体的形式包括:

(1)确定起点的最短路径问题:即已知起始结点,求最短路径的问题。

(2)确定终点的最短路径问题:与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。

(3)确定起点终点的最短路径问题:即已知起点和终点,求两结点之间的最短路径。

(4)全局最短路径问题:求图中所有的最短路径。

用于解决最短路径问题的算法被称做“最短路径算法”, 有时被简称作“路径算法”。 最常用的路径算法有:Dijkstra算法、A*算法、Bellman-Ford算法、Floyd-Warshall算法、Johnson算法。 

2  Dijkstra算法

2.1  Dijkstra算法

  Dijkstra算法是典型最短路算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低

Dijkstra算法是很有代表性的最短路算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。 

2.2  Dijkstra算法思想

Dijkstra算法思想为:

           设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,

         第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将 加入到集合S中,直到全部顶点都加入到S中,算法就结束了),

          第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。

            此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。

2.3  Dijkstra算法具体步骤  

1)初始时,S只包含源点,即S=,v的距离为0。U包含除v外的其他顶点,U中顶点u距离为边上的权(若v与u有边)或 )(若u不是v的出边邻接点)。

(2)从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。

(3)以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u(u U)的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。

(4)重复步骤(2)和(3)直到所有顶点都包含在S中。

2.4  Dijkstra算法举例说明

如下图,设A为源点,求A到其他各顶点(B、C、D、E、F)的最短路径。线上所标注为相邻线段之间的距离,即权值。(注:此图为随意所画,其相邻顶点间的距离与图中的目视长度不能一一对等)

图一:Dijkstra无向图

 最短路径之Dijkstra算法详细讲解 - 绿岩 - 永远的绿岩

算法执行步骤如下表:【注:图片要是看不到请到“相册--日志相册”中,名为“Dijkstra算法过程”的图就是了】



=========================  转自最短路径之Dijkstra算法 =========================

dijkstra算法两个应用题:

HDOJ 1874 畅通工程续,现有解法:www.wutianqi.com/?p=1894
HDOJ 2544 最短路,现有解法:www.wutianqi.com/?p=1892

参考:http://hi.baidu.com/zealot886/item/c8a499ee5795bcddeb34c950

           数据结构(C语言版)

          http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html

推荐几篇搜索算法相关的非常好的博文:

一、A*搜索算法

一(续)、A*,Dijkstra,BFS算法性能比较及A*算法的应用

二、Dijkstra 算法初探          (Dijkstra算法系列4篇文章)

二(续)、彻底理解Dijkstra算法

二(再续)、Dijkstra 算法+fibonacci堆的逐步c实现

二(三续)、Dijkstra 算法+Heap堆的完整c实现源码



==============

代码

 

#include <stdio.h>
#include <malloc.h>
#define VERTEXNUM 6

//存放最短路径的边元素
typedef struct edge{
        int vertex;
        int value;
        struct edge* next;
}st_edge;

void createGraph(int (*edge)[VERTEXNUM], int start, int end, int value);
void displayGraph(int (*edge)[VERTEXNUM]);
void displayPath(st_edge** path, int startVertex,int* shortestPath);
void dijkstra(int (*edge)[VERTEXNUM], st_edge*** path, int** shortestPath, int startVertex, int* vertexStatusArr);
int getDistance(int value, int startVertex, int start, int* shortestPath);
void createPath(st_edge **path, int startVertex, int start, int end, int edgeValue);

int main(void){
        //动态创建存放边的二维数组
        int (*edge)[VERTEXNUM] = (int (*)[VERTEXNUM])malloc(sizeof(int)*VERTEXNUM*VERTEXNUM);
        int i,j;
        for(i=0;i<VERTEXNUM;i++){
                for(j=0;j<VERTEXNUM;j++){
                        edge[i][j] = 0;
                }
        }
        //存放顶点的遍历状态,0:未遍历,1:已遍历
        int* vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM);
        for(i=0;i<VERTEXNUM;i++){
                vertexStatusArr[i] = 0;
        }

        printf("after init:\n");
        displayGraph(edge);
        //创建图
        createGraph(edge,0,1,6);
        createGraph(edge,0,3,5);
        createGraph(edge,0,2,1);
        createGraph(edge,1,2,5);
        createGraph(edge,1,4,3);
        createGraph(edge,2,4,6);
        createGraph(edge,2,3,5);
        createGraph(edge,2,5,4);
        createGraph(edge,3,5,2);
        createGraph(edge,4,5,6);

        printf("after create:\n");
        displayGraph(edge);
    //最短路径
        /*存储的结构如下:
            path[0]:edge0->NULL
            path[1]:edge1->NULL
            path[2]:edge1->edge2->NULL
            path[3]:edge1->edge2->edge3->NULL
            path[4]:edge4->NULL
            从顶点0到0的最短路径:从0出发直接到0
            从顶点0到1的最短路径:从0出发直接到1
            从顶点0到2的最短路径:从0出发到1,从1出发到2
            ......
        */
    st_edge** path = NULL;
    //存储最短路径的权值
        /*
        shortestPath[0] = 0;
        shortestPath[1] = 8;
        shortestPath[2] = 12;
        从顶点0到0的路径是0
        从顶点0到1的路径是8
        从顶点0到2的路径是12
        */
    int* shortestPath = NULL;
    //从顶点0开始寻找最短路径
    int startVertex = 0;
    //最短路径
    dijkstra(edge, &path, &shortestPath, startVertex, vertexStatusArr);
    printf("the path is:\n");
    displayPath(path,startVertex,shortestPath);

        free(edge);
        free(path);
        return 0;
}
//创建图
void createGraph(int (*edge)[VERTEXNUM], int start, int end, int value){
        edge[start][end] = value;
        edge[end][start] = value;
}
//打印存储的图
void displayGraph(int (*edge)[VERTEXNUM]){
        int i,j;
        for(i=0;i<VERTEXNUM;i++){
                for(j=0;j<VERTEXNUM;j++){
                        printf("%d ",edge[i][j]);
                }
                printf("\n");
        }
}
//打印最短路径
void displayPath(st_edge** path, int startVertex,int* shortestPath){
        int i;
        st_edge* p;
        for(i=0;i<VERTEXNUM;i++){
                printf("Path from %d to %d:",startVertex,i);
                p = *(path+i);
                while(p != NULL){
                        printf("%d(%d) ",p->vertex,p->value);
                        p = p->next;
                }
                printf("\n");
        printf("the count is:%d\n",shortestPath[i]);
        }
}
//最短路径
void dijkstra(int (*edge)[VERTEXNUM], st_edge*** path, int** shortestPath, int startVertex, int* vertexStatusArr){
    //初始化最短路径
    *path = (st_edge**)malloc(sizeof(st_edge*)*VERTEXNUM);
        int i,j;
    for(i=0;i<VERTEXNUM;i++){
        if(i == startVertex){
            st_edge* e = (st_edge*)malloc(sizeof(st_edge));
            e->vertex = startVertex;
            e->value = 0;
            e->next = NULL;
            (*path)[i] = e;
        }else{
            (*path)[i] = NULL;
        }
    }
    //初始化最短路径的权值
    *shortestPath = (int *)malloc(sizeof(int)*VERTEXNUM);
    for(i=0;i<VERTEXNUM;i++){
        if(i == startVertex){
            (*shortestPath)[i] = 0;
        }else{
            (*shortestPath)[i] = -1;
        }
    }
    //从顶点0开始,则顶点0就是已访问的
    vertexStatusArr[startVertex] = 1;

    int shortest, distance,start, end, edgeValue, vNum = 1;
        //如果还顶点还没有访问完
        while(vNum < VERTEXNUM){
                shortest = 9999;
                for(i=0;i<VERTEXNUM;i++){
                        //选择已经访问过的点
                        if(vertexStatusArr[i] == 1){
                                for(j=0;j<VERTEXNUM;j++){
                                        //选择一个没有访问过的点
                                        if(vertexStatusArr[j] == 0){
                                                //选出一条value最小的边
                                                if(edge[i][j] != 0 && (distance = getDistance(edge[i][j], startVertex, i,  *shortestPath)) < shortest){
                                                        shortest = distance;
                                                        edgeValue = edge[i][j];
                                                        start = i;
                                                        end = j;
                                                }
                                        }
                                }
                        }
                }
                vNum++;
            //将点设置为访问过
            vertexStatusArr[end] = 1;
            //保存最短路径权值
            (*shortestPath)[end] = shortest;
            //保存最短路径
            createPath(*path, startVertex, start, end, edgeValue);
        }
}

//返回从startVertex到新的顶点的距离
int getDistance(int value, int startVertex, int start, int* shortestPath){
    if(start == startVertex){
        return value;
    }else{
        return shortestPath[start] + value;
    }
}

//保存最短路径
void createPath(st_edge **path, int startVertex, int start, int end, int edgeValue){
    if(start == startVertex){
        st_edge* newEdge = (st_edge*)malloc(sizeof(st_edge));
        newEdge->vertex = end;
        newEdge->value = edgeValue;
        newEdge->next = NULL;

        st_edge** p = path + end;
        while((*p) != NULL){
            p = &((*p)->next);
        }
        *p = newEdge;
    }else{
        st_edge** pCopySrc = path + start;
        st_edge** pCopyDes = path + end;
        st_edge* newEdge = NULL;
        while((*pCopySrc) != NULL){
            newEdge = (st_edge*)malloc(sizeof(st_edge));
            *newEdge = **pCopySrc;
            newEdge->next = NULL;
            *pCopyDes = newEdge;
            pCopySrc = &((*pCopySrc)->next);
            pCopyDes = &((*pCopyDes)->next);
        }
        newEdge = (st_edge*)malloc(sizeof(st_edge));
        newEdge->vertex = end;
        newEdge->value = edgeValue;
        newEdge->next = NULL;
        *pCopyDes = newEdge;
    }
}



讲解 Dijkstra 算法的基本思想,另外还有算法实现. 当然了,这个算法当路径点上万的时候效率上会降低。 我有另外的改进实现, 上万个点也是在200毫秒内完成。但是不知道怎么添加, 我只能在这里贴关键代码了 : static std::list<Node*> vecNodes; static std::list<Edge*> vecEdges; bool CDijkstras::DijkstrasFindPath(Node* psrcNode, Node* pdstNode, std::list<Node*>& vec, double& fromSrcDist) { if (psrcNode == 0 || pdstNode == 0) return false; if (psrcNode == pdstNode) { vec.push_back(pdstNode); return false; } std::list<Node*>::const_iterator it; for (it=vecNodes.begin(); it!=vecNodes.end(); it++) { (*it)->bAdded = false; (*it)->previous = 0; (*it)->distanceFromStart = MAXDOUBLE; (*it)->smallest = 0; } bool bFindDst = DijkstrasRouteInitialize(psrcNode, pdstNode); fromSrcDist = pdstNode->distanceFromStart; Node* previous = pdstNode; while (previous) { vec.push_back(previous); previous = previous->previous; } m_pDstNode = pdstNode; return bFindDst; } bool CDijkstras::DijkstrasRouteInitialize(Node* psrcNode, Node* pdstNode) { bool bFindDst = false; psrcNode->distanceFromStart = 0; Node* smallest = psrcNode; smallest->bAdded = true; std::list<Node*>::const_iterator it, ait; std::list<Node*> AdjAdjNodes ; for (it=psrcNode->connectNodes.begin(); it!=psrcNode->connectNodes.end(); it++) { if ((*it)->bAdded) continue; (*it)->smallest = psrcNode; (*it)->bAdded = true; AdjAdjNodes.push_back(*it); } while (1) { std::list<Node*> tempAdjAdjNodes; for (it=AdjAdjNodes.begin(); it!=AdjAdjNodes.end(); it++) { Node* curNode = *it; for (ait=curNode->connectNodes.begin(); ait!=curNode->connectNodes.end(); ait++) { Node* pns = *ait; double distance = Distance(pns, curNode) + pns->distanceFromStart; if (distance < curNode->distanceFromStart) { curNode->distanceFromStart = distance; curNode->previous = pns; } if (pns->bAdded == false) { tempAdjAdjNodes.push_back(pns); pns->bAdded = true; } } if (curNode == pdstNode) { bFindDst = true; } } if (bFindDst) break; if (tempAdjAdjNodes.size() == 0) break; AdjAdjNodes.clear(); AdjAdjNodes = tempAdjAdjNodes; } return bFindDst; } // Return distance between two connected nodes float CDijkstras::Distance(Node* node1, Node* node2) { std::list<Edge*>::const_iterator it; for (it=node1->connectEdges.begin(); it!=node1->connectEdges.end(); it++) { if ( (*it)->node1 == node2 || (*it)->node2 == node2 ) return (*it)->distance; } #ifdef _DEBUG __asm {int 3}; #endif return (float)ULONG_MAX; } /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ //得到区域的Key// __int64 CDijkstras::GetRegionKey( float x, float z ) { long xRegion = (long)(x / m_regionWidth); long zRegion = (long)(z / m_regionHeight); __int64 key = xRegion; key <<= 32; key |= ( zRegion & 0x00000000FFFFFFFF ); return key; } //得到区域的Key// __int64 CDijkstras::GetRegionKey( long tx, long tz ) { long xRegion = tx ; long zRegion = tz ; __int64 key = xRegion; key <<= 32; key |= ( zRegion & 0x00000000FFFFFFFF ); return key; } //取得一个区域内的所有的路径点, 返回添加的路径点的个数// unsigned long CDijkstras::GetRegionWaypoint (__int64 rkey, std::vector<Node*>& vec) { unsigned long i = 0; SAME_RANGE_NODE rangeNode = mmapWaypoint.equal_range(rkey); for (CRWPIT it=rangeNode.first; it!=rangeNode.second; it++) { i++; Node* pn = it->second; vec.push_back(pn); } return i; } inline bool cmdDistanceNode (Node* pNode1, Node* pNode2) { return pNode1->cmpFromStart < pNode2->cmpFromStart; }; //添加一个路径点// Node* CDijkstras::AddNode (unsigned long id, float x, float y, float z) { Node* pNode = new Node(id, x, y, z); __int64 rkey = GetRegionKey(x, z); mmapWaypoint.insert(make_pair(rkey, pNode)); mapID2Node[id] = pNode; return pNode; } //添加一条边// Edge* CDijkstras::AddEdge (Node* node1, Node* node2, float fCost) { Edge* e = new Edge (node1, node2, fCost); return e; } //通过路径点ID得到路径点的指针// Node* CDijkstras::GetNodeByID (unsigned long nid) { std::map<unsigned long, Node*>::const_iterator it; it = mapID2Node.find(nid); if (it!=mapID2Node.end()) return it->second; return NULL; }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值