Dijkstra算法

适用范围

广度优先搜索可在非加权图中查找最短路径,Dijkstra算法是可在加权图中解决单源最短路径问题。

仅当权重为正的时候Dijkstra算法才管用,带有负权边的图不能够使用Dijkstra算法,应该使用Bellman-Ford算法

主要思路

  1. 找出最近结点
  2. 更新这个结点邻接点的开销
  3. 重复步骤1,步骤2,直到对图中所有结点都进行了这两个步骤
  4. 计算最终路径

实例介绍

8
5
7
2
2
4
6
1
3
1
2
3
4
5
6

假设我们需要找出从结点1到结点6的最短路径
为了实现我们刚才设想的思路,我们需要记录三种信息,可分别用三个散列表来记录:

  1. 图的信息:每个结点和它的邻接点
  2. 实时的距离信息:当前从起点到某点的距离为多少(后文会详细解释)
  3. 结点的父节点:路径上该结点的前一个结点

初始化表一edges(图的信息):

结点邻接点边权重
122
135
238
247
546
354
342
461
563

初始化表二dist(已有的距离信息):
在进行算法之前,起点到每个结点的距离都为无穷大

结点距离
10
2
3
4
5
6

初始化表三par(结点的父节点):
在进行算法之前,可将每个结点的父节点都设为未知结点

结点父节点
10
20
30
40
50
60

此外我们还需要一个表来记录某结点是否已经处理过,因为对一个结点重复处理是没有意义的。我们可以用一个向量known来记录已经处理过的结点

known

1.找出最近结点
找出最近节点:找出表二中距离最近且未处理过的结点,此时只有起点的距离为0,其他结点的距离为无穷大,因此我们先处理起点,并将起点添加到known中,表示已经处理;

2.更新这个结点邻接点的开销
起点到结点2的距离为2,该距离小于结点2的已有距离无穷大,故更新结点2的已有距离为2,同理将结点3的已有距离更新为5

结点距离
10
22
35
4
5
6
known1

重复步骤1,2直到每个结点都已经处理过
后面为了简便叙述将表二表三和表示已经处理结点的表放在一起

结点距离(dist)父节点(par)是否处理过
100true
221false
351false
40false
50false
60false

1.找出最近结点
此时最近的未处理结点是结点2,我们将其标记为已处理。

known12

2. 更新这个结点邻接点的开销
结点2到结点3的距离为8,此时需要判断的是否需要更新结点3的距离信息,结点3的已有距离是5,也就是说目前从起点到达结点3的最短距离是5。那么是否从起点经过结点2再到达结点3更近呢,

  • dist[3] = 5
  • dist[2] + edges[2][3] = 2 + 8 = 10
  • 5 < 10
  • 从起点直接到结点3更近,因此无需更新

同时结点2到结点4还有一条距离为7的边,也是同样的比较方法

  • dist[4] = ∞
  • dist[2] + edges[2][4] = 2 + 7 = 9
  • 9 < ∞
  • 更新dist[4] = 9,par[4] = 2
结点距离(dist)父节点(par)是否处理过
100true
221true
351false
492false
50false
60false

我们重复步骤1和步骤2,直到所有结点都已处理。随后的处理如下:

1.找出最近结点

结点距离(dist)父节点(par)是否处理过
100true
221true
351true
492false
50false
60false

2. 更新这个结点邻接点的开销

结点距离(dist)父节点(par)是否处理过
100true
221true
351true
473false
593false
60false

1.找出最近结点

结点距离(dist)父节点(par)是否处理过
100true
221true
351true
473true
593false
60false

2. 更新这个结点邻接点的开销

结点距离(dist)父节点(par)是否处理过
100true
221true
351true
473true
593false
684false

1.找出最近结点

结点距离(dist)父节点(par)是否处理过
100true
221true
351true
473true
593false
684true

2. 更新这个结点邻接点的开销

结点6无邻接点

1.找出最近结点

结点距离(dist)父节点(par)是否处理过
100true
221true
351true
473true
593true
684true

2. 更新这个结点邻接点的开销
结点5和结点4与结点6邻接

  • dist[5] + edges[5][4] = 9 + 6 = 15 > dist[4]
  • 结点4无需更新
  • dist[5] + edges[5][6] = 9 + 3 = 12 > dist[6]
  • 结点6无需更新
结点距离(dist)父节点(par)是否处理过
100true
221true
351true
473true
593true
684true

至此所有的结点都已处理

known123465

我们可以输出dist[6] = 8表示起点到结点6的最短距离为8。那么如何输出整条路径呢,由于起点的父节点为0,我们可以通过递归的方法输出

void print_path(char node)
{
    if(par[node] == '0')
    {
        cout<<"the distance is "<<dist[end]<<endl;
        cout<<node;
    }
    else
    {
        print_path(par[node]);
        cout<<"->"<<node;
    }
}

代码实现

/**
* @author jump
* @description Dijkstra Algorithm
* @created 2021-02-02
* @last-modified 2021-02-15
*/

#include <bits/stdc++.h>
using namespace std;
const int inf = 666;
class Gragh
{
public:
    map<char, map<char, int>> edges;
    map<char, int> dist;
    map<char, char> par;
    vector<char> known;
    int size;
    char start;
    char end;
    char find_smallest_cost_node()
    {
        char ans = '@';
        int lowest_cost = inf;
        for (auto t : dist)
        {
            if (find(known.begin(), known.end(), t.first) == known.end()) //!not in
            {
                if (t.second < lowest_cost)
                {
                    lowest_cost = t.second;
                    ans = t.first;
                }
            }
        }
        return ans;
    }
    void init()
    {
        char start, end;
        int dist, edge;
        cout << "how many nodes?" << endl
             << "how many edges?" << endl
             << "please input the list" << endl;
        cin >> size >> edge;
        //^in such format: beginning ending weight
        while (edge--)
        {
            cin >> start >> end >> dist;
            edges[start][end] = dist;
            Gragh::dist[start] = inf;
            par[start] = '0';
        }
    }
    void set()
    {
        cout << "please set the starting node and ending node" << endl;
        cin >> start >> end;
        dist[start] = 0;
        edges[end] = {};
        dist[end] = inf;
        par[end] = '0';
    }
    void Dijkstra()
    {
        while (1)
        {
            char node = find_smallest_cost_node();
            if (node == '@')
                break;
            known.push_back(node);
            for (auto t : edges[node])
            {
                if (find(known.begin(), known.end(), t.first) == known.end() && dist[node] + t.second < dist[t.first]) //^update
                {
                    dist[t.first] = t.second + dist[node];
                    par[t.first] = node;
                }
            }
        }
    }
    //another way to print the path:
    /* void output()
    {
        stack<char> lst;
        while (end != '0')
        {
            lst.push(end);
            end = par[end];
        }
        while (!lst.empty())
        {
            end = lst.top();
            cout << end;
            lst.pop();
            if (lst.empty())
                break;
            cout << "->";
        }
        cout << endl
             << "The distance is:" << dist[end] << endl;
    } */
    void print_path(char node)
    {
        if (par[node] == '0')
        {
            cout<<"the distance is "<<dist[end]<<endl;
            cout << node;
        }
        else
        {
            print_path(par[node]);
            cout << "->" << node;
        }
    }
};
int main()
{
    Gragh g;
    g.init();
    g.set();
    g.Dijkstra();
    g.print_path(g.end);
}
/* test input : 
6
9
1 2 2
1 3 5 
2 3 8
2 4 7
3 4 2
3 5 4
5 4 6
4 6 1
5 6 3
1 6
 */

输入输出样例如下图所示
样理输出
欢迎指正

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值