最短路径dijkstra算法及其优先队列的优化

最短路径问题:对任意给出的图G(V,E)和起点S,终点T,如何求从S到T的最短路径。解决最短路径问题的算法有Dijkstra算法,Bellman-Ford算法,SPFA算法和Floyd算法。

Dijkstra算法描述

主要用于解决单源最短路径问题,即给定图G(V,E)和起点s,通过算法得到S到达其他每个顶点的最短距离。

算法步骤:设置集合S存放已被访问的顶点,然后执行n次下面的每个步骤(n为顶点个数)

(1)每次从集合V-S(未被访问的顶点)中选择与起点S的最短距离最小的一个顶点记为u,访问并加入集合s

(2)之后,令顶点u为中介点,优化起点S与所有从u可以到达的顶点v之间的最短距离。

集合S可以用一个布尔型数组来实现,当vis[i]==true时表示顶点vi已被访问。

令int型数组d[]表示从起点到任意顶点的最短距离,初始时给起点s的d[s]赋值为0,其余为很大的值,可以用二进制编码的十六进制0x3fffffff来表示inf,即不可到达。

伪代码实现

Dijkstra(G,d[],s)
{
    初始化;
    for(循环n次)
    {
        u=使d[u]最小的还未被访问过的顶点的标号;
        记u已被访问;
        for(从u出发能到达的所有顶点v)
        {
            if(v未被访问&&以u为中介点使s到顶点v的最短距离最优)
            {
                优化d[v];
            }
        }
    }
}

邻接矩阵代码实现(适用于点数不超过1000)

const int maxn=1000;
const int INF=0x3fffffff;

int n,G[maxn][maxn];
int d[maxn];
bool vis[maxn]={false};

void Dijkstra(int s)
{
    fill(d,d+maxn,INF);
    d[s]=0;
    for(int i=0;i

时间复杂度为O(n^2)

邻接表版代码实现

const int INF=0x3fffffff;
const int maxn=10000;

struct node
{
    int v;
    int dis;
};

vector adj[maxn];
int n;
int d[maxn];
bool vis[maxn]={false};

void Dijkstra(int s)
{
    fill(d,d+maxv,INF);  //慎用memset
    d[s]=0;
    for(int i=0;i

时间复杂度同上。这2种做法的时间复杂度都是O(n^2)。由于需要把每条边都标记为已访问,因此外层循环的O(n)无法避免,但是寻找最小d[u]的过程可以不必达到O(n)的复杂度,可以使用堆优化的方式来降低复杂度,最简练的方式是使用优先队列priority_queue来实现。这样时间复杂度将为O(nlogn).

使用堆优化

const int maxv=1000;
const int INF=0x3fffffff;

struct node
{
    int v,dis;
    bool friend operator < (node n1,node n2)   //设置优先级,即距离小的优先级高
    {
        return n1.dis>n2.dis;
    }
};

vector adj[maxv];
priority_queue q;    //优先队列
bool vis[maxv];
int dis[maxv];

int n,m,s;  //点数,边数和 起点下标

void Dij(int s)
{
    dis[s]=0;   //起点最小距离为0
    node now;
    now.v=s;
    now.dis=0;
    q.push(now);   //放入起点
    while(!q.empty())
    {
        node topnode=q.top();
        q.pop();
        if(!vis[topnode.v])
        {
            vis[topnode.v]=1;
            for(int i=0;i

最小堆保证每次取出的都是距离最小的,可能会重复放进队列,但是如果已经访问过就不会再访问。

案例一:

输入:第一行给出3个整数,m,n,t;分别表示m个顶点,n条边,起点为t。接下来n行输入,每行给出边的起点终点和边权。需要输出起点到每个顶点的最短距离。

未改进版:

#include 
#include 
#include 
#include 
#include 

using namespace std;

struct node
{
    int v,dis;
};

const int maxv=1000;  //最大顶点数
vector adj[maxv];    //邻接表
int dis[maxv];   //存放最短距离
int n,m,s;  //顶点数,边数,起点下标
bool vis[maxv];  //记录是否还没有被访问

const int INF=0x3fffffff;

void Dij(int s)
{
    dis[s]=0;
    for(int i=0;i

最短路径dijkstra算法及其优先队列的优化_第1张图片

优先队列改进版:

#include 
#include 
#include 
#include 

using namespace std;
const int maxv=1000;
const int INF=0x3fffffff;

struct node
{
    int v,dis;
    bool friend operator < (node n1,node n2)   //设置优先级
    {
        return n1.dis>n2.dis;
    }
};

vector adj[maxv];
priority_queue q;
bool vis[maxv];
int dis[maxv];

int n,m,s;  //点数,边数和 起点下标

void Dij(int s)
{
    dis[s]=0;
    node now;
    now.v=s;
    now.dis=0;
    q.push(now);
    while(!q.empty())
    {
        node topnode=q.top();
        q.pop();
        if(!vis[topnode.v])
        {
            vis[topnode.v]=1;
            for(int i=0;i

最短路径dijkstra算法及其优先队列的优化_第2张图片

 

你可能感兴趣的:(数据结构)