dijstra算法

dijstra

主要使用场景场景

  • 计算边权为正的图的单源最短路径

注意事项

  1. 边权必须为正
  2. dijstra算法的处理对象是图,图可以以各种方式保存,这里采用邻接矩阵的方式
  3. 这个算法把图和一对节点输入,并把最短路径写在path数组数组里.(path实际保存的是当前节点在最短路径的下一个节点)
  4. 如果想检测到目的地就停止可以在每次循环的末尾判断destination的邻接节点是否都known==true是则可以结束.

结构

  1. dijstra算法需要三个数组,
  • vector<bool> known;标记已经考虑过的节点.
  • vector<int> cost;标记这轮比较前的花费,
  • vector<vector<int>> path标记最短路里当前节点的前一个节点,这里可能有多条,因此path采用了vector<vector<int>>的形式保存.

模板

结构模板

主体

主体相对简单包含初始化和遍历两个函数

void dijstra(vector<vector<int>> graph, vector<vector<int>> &path, int departure, int destination)
{
    int size = graph.size();
    vector<bool> known;
    vector<int> cost;
    initiate(known, cost, path, departure);
    loop(graph, known, cost, path, destination);
}
初始化函数

这里需要对用到的三个数组进行初始化,known全标记未知(false)cost全标记最大代表还没有路,path全标记-1代表没有前缀.
同时需要把初始节点的cost变为最小(0);

void initiate(vector<bool> &known, vector<int> &cost, vector<vector<int>> &path)
{
    for (int i = 0; i < known.size(); i++)
    {
        known[i] = false;//标记未知
    }
    for (int i = 0; i < cost.size(); i++)
    {
        cost[i] = INT_MAX;//标记无路
    }
    for (int i = 0; i < path.size(); i++)
    {
        for (int j = 0; j < path[i].size(); j++)
        {
            path[i][j] = NO_PREVERTEX_SIGN;//标记没有前缀.
        }
    }
}
遍历函数

其实主要的操作数组是cost,遍历也是以cost的大小进行遍历

  1. 每次在cost里找最小
  2. 标记访问,进行访问,找临边
  3. 临边的花费如果比本点的花费与本点到临边的路径和大则更新邻接点的costpath,等于则加入parh,小于不做事.
void loop(vector<vector<int>> graph, vector<bool> &known, vector<int> &cost, vector<vector<int>> &path, int destination)
{
    int size = graph.size();
    for (int i = 0; i < size; i++) //单纯计数,里面用的都是minIndex
    {
        int minIndex = INT_MAX;
        for (int i = 0; i < cost.size(); i++) //在未访问过的里找花费最小的.
        {
            if (known[i] == false && cost[i] < minIndex)
            {
                minIndex = i;
            }
        }
        known[minIndex] = true;                          //标记访问并开始访问.
        for (int j = 0; j < graph[minIndex].size(); j++) //遍历最小花费点的邻接点
        {
            if (graph[minIndex][j] != 0) //有边
            {
                if (cost[j] > cost[minIndex] + graph[minIndex][j]) //有更小的时,改变cost重置path
                {
                    cost[j] = cost[minIndex] + graph[minIndex][j];
                    path[j].clear();
                    path[j].push_back(minIndex);
                    continue; //这里改变了cost[j]但是下面还要用,因此需要continue,以免改过了还要进下面if.
                }
                if (cost[j] == cost[minIndex] + graph[minIndex][j]) //相等时,加入到path里
                {
                    path[j].push_back(minIndex);
                }
            }
        }
        /*         需要及时停止有这一段,遍历全图不用这一段.                       */
        int j = 0;
        for (; j < graph[destination].size(); j++)
        {
            if (graph[destination][j] != 0) //有边
            {
                if (known[j] == false)
                {
                    break;
                }
            }
        }
        if (j == graph[destination].size()) //邻接全都访问过
        {
            return;
        }
    }
}

实现

cpp

#include <bits/stdc++.h>
#include <vector>

using namespace std;
const int vertexSize = 100;
const int NO_PREVERTEX_SIGN = -1;
void initiate(vector<bool> &known, vector<int> &cost, vector<vector<int>> &path, int departure)
{
    for (int i = 0; i < known.size(); i++)
    {
        known[i] = false; //标记未知
    }
    for (int i = 0; i < cost.size(); i++)
    {
        cost[i] = INT_MAX; //标记无路
    }
    cost[departure] = 0;
    for (int i = 0; i < path.size(); i++)
    {
        for (int j = 0; j < path[i].size(); j++)
        {
            path[i][j] = NO_PREVERTEX_SIGN; //标记没有前缀.
        }
    }
}
void loop(vector<vector<int>> graph, vector<bool> &known, vector<int> &cost, vector<vector<int>> &path, int destination)
{
    int size = graph.size();
    for (int i = 0; i < size; i++) //单纯计数,里面用的都是minIndex
    {
        int minIndex = INT_MAX;
        for (int i = 0; i < cost.size(); i++) //在未访问过的里找花费最小的.
        {
            if (known[i] == false && cost[i] < minIndex)
            {
                minIndex = i;
            }
        }
        known[minIndex] = true;                          //标记访问并开始访问.
        for (int j = 0; j < graph[minIndex].size(); j++) //遍历最小花费点的邻接点
        {
            if (graph[minIndex][j] != 0) //有边
            {
                if (cost[j] > cost[minIndex] + graph[minIndex][j]) //有更小的时,改变cost重置path
                {
                    cost[j] = cost[minIndex] + graph[minIndex][j];
                    path[j].clear();
                    path[j].push_back(minIndex);
                    continue; //这里改变了cost[j]但是下面还要用,因此需要continue,以免改过了还要进下面if.
                }
                if (cost[j] == cost[minIndex] + graph[minIndex][j]) //相等时,加入到path里
                {
                    path[j].push_back(minIndex);
                }
            }
        }
        /*         需要及时停止有这一段,遍历全图不用这一段.                       */
        int j = 0;
        for (; j < graph[destination].size(); j++)
        {
            if (graph[destination][j] != 0) //有边
            {
                if (known[j] == false)
                {
                    break;
                }
            }
        }
        if (j == graph[destination].size()) //邻接全都访问过
        {
            return;
        }
    }
}
void dijstra(vector<vector<int>> graph, vector<vector<int>> &path, int departure, int destination)
{
    int size = graph.size();
    vector<bool> known;
    vector<int> cost;
    initiate(known, cost, path, departure);
    loop(graph, known, cost, path, destination);
}

int main(int argc, char const *argv[])
{
    vector<vector<int>> graph;
    graph.resize(vertexSize);
    for (int i = 0; i < vertexSize; i++)
    {
        graph[i].resize(vertexSize);
    }
    vector<vector<int>> path;
    int departure = 0;
    int destination = 99;
    dijstra(graph, path, departure, destination);
    return 0;
}

经典问题

正权单源最短路

问题描述
例题演示

PTA 1018 Public Bike Management (30分)[DFS][单源最短路径]

实现
cpp

参考文献

  1. 我滴脑子
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值