算法基础——Dijkstra算法


算法基础—Dijkstra算法


brycezou@163.com


1、最优子结构


        如果 d(i,j)={Vi,,Vm,,Vn,,Vj} 是从顶点 i j 的最短路径, m n 是该路径上的2个中间节点,那么 d(m,n) 必定是从 m n 的最短路径。

        用反证法进行证明:假设 d(i,j) 是从顶点 i j 的最短路径,则 d(i,j)=d(i,m)+d(m,n)+d(n,j) 。而由于 d(m,n) 不是从 m n 的最短距离,则必定存在另一条从 m n 的最短路径 d(m,n) ,使得 d(i,j)=d(i,m)+d(m,n)+d(n,j)<d(i,j) ,这与 d(i,j) 是从 i j 的最短路径相矛盾。因此, d(m,n) 必定是从 m n 的最短路径。

        由最短路径的最优子结构性质可知,如果存在一条从 i j 的最短路径 (Vi,,Vk,Vj) ,其中, Vk Vj 前驱节点,那么 (Vi,,Vk) 必定是从 i k 的最短路径。为了求出最短路径,Dijkstra提出了以最短路径长度递增,逐次生成最短路径的算法。最短路径的更新公式为
dist[u]=min{dist[u], dist[k]+graph[k][u]}

其中, dist[u] 用来记录从源点 V0 到顶点 Vu 的最短路径的长度。

2、Dijkstra算法


        Dijkstra算法的基本思想:设 G=(V,E) 是一个带权有向图,把图中的顶点集合 V 分成两组,第一组为已求出最短路径的顶点集合,用 S 表示,初始时 S 中只有一个源点,以后每求得一条最短路径,就将其对应的顶点加入到 S 中,直到全部顶点都加入到 S ;第二组为其余未确定最短路径的顶点集合,用 U 表示,按最短路径长度递增的次序依次把这些顶点加入到 S 。在加入的过程中,总保持从源点 V0 S 中各顶点的最短路径长度不大于从 V0 U 中任何顶点的最短路径长度。

       算法步骤:


        a)初始时,集合 S 只包含源点,即 S{V0} ,且 dist[V0]=0 。集合 U 包含除 V0 以外的其他顶点。若 V0 U 中的顶点 u 有边,则 dist[u]=graph[V0][u] ,有正常的权值;否则 dist[u]=

        b)从 U 中选取一个与 V0 距离最近的顶点 k ,把 k 加入 S 中,该距离就是从 V0 k 的最短路径的长度。

        c)以 k 为中间点,修改 U 中各顶点的距离——若从源点 V0 到顶点 u 的距离(经过顶点 k)比原来的距离(不经过顶点 k )短,则更新顶点 u 的距离值。

        d)重复步骤 b 和 c 直到所有顶点都包含在 S <script type="math/tex" id="MathJax-Element-148">S</script> 中。

3、算法代码示例


        本算法代码,旨在清晰地展示算法细节,而未进行任何优化,请多多包涵。


#include <iostream>
#include <stack>

using namespace std;


#define MAX_VERTEX  5
#define MAX_DIST    65535

void show_path(int prev_node[],int v0,int u)   //打印最短路径上的各个顶点
{
    stack<int> stk;
    while(u != v0)
    {
        stk.push(u);
        u = prev_node[u];  //取前驱节点
    }
    stk.push(u);
    while(!stk.empty())
    {
        cout<<stk.top()<<" ";
        stk.pop();
    }
}

int graph[MAX_VERTEX][MAX_VERTEX];
int dist[MAX_VERTEX];
int prev_node[MAX_VERTEX];
bool visited[MAX_VERTEX];

int main(int argc, char *argv[])
{
    cout << "Hello Dijkstra!" << endl;

    for(int i = 0; i < MAX_VERTEX; i++)
    {
        for(int j = 0; j <= i; j++)
        {
            if(i==j)
                graph[i][j] = 0;
            else
                graph[i][j] = graph[j][i] = MAX_DIST;
        }
    }
    graph[0][1] = 100;
    graph[0][2] = 30;
    graph[0][4] = 10;
    graph[2][1] = 60;
    graph[2][3] = 60;
    graph[3][1] = 10;
    graph[4][3] = 50;

    int v0 = 0;
    for(int i = 0; i < MAX_VERTEX; i++)
    {
        dist[i] = MAX_DIST;
        prev_node[i] = -1;
        visited[i] = false;
        if(graph[v0][i] < MAX_DIST && i != v0)
        {
            dist[i] = graph[v0][i];
            prev_node[i] = v0;
        }
    }

    dist[v0] = 0;
    prev_node[v0] = v0;
    visited[v0] = true;

    for(int k = 1; k < MAX_VERTEX; k++)  //循环扩展N-1次
    {
        int min_dist = MAX_DIST;
        int u;
        for(int i = 0; i < MAX_VERTEX; i++)  //寻找未被访问的权值最小的顶点
        {
            if((!visited[i]) && (dist[i] < min_dist))
            {
                min_dist = dist[i];
                u = i;
            }
        }

        visited[u] = true;

        for(int i = 0; i < MAX_VERTEX; i++)  //更新dist数组的值和路径的值
        {
            if((!visited[i]) && (graph[u][i] < MAX_DIST) && (dist[u]+graph[u][i] < dist[i]))
            {
                dist[i] = dist[u]+graph[u][i];
                prev_node[i] = u;  //记录前驱顶点
            }
        }
    }

    for(int i = 0; i < MAX_VERTEX; i++)
    {
        if(i != v0)
        {
            show_path(prev_node, v0, i);
            cout<<dist[i]<<endl;
        }
    }

    cout<<""<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值