Dijkstra算法(最小路)

在图论中的一个经典求最短路的算法,基本上,所有的数据结构的书籍都会有这个算法,这算法有一个特性,我要求一个点到另一个点的最短距离,这个算法不仅给出这个最短距离还有很多“副产品”,也就是说只要在图中所有与该点连通的点,那么这些点的最短路会一并求出。
适用条件:
1.所有所有权边均为正。
2.有向/无向。
这里我们用邻接表来描述(存储)图。设点为1到n编号的店,m条边,与之对应的m个权值,x为起点(这里只考虑简单图)

Dijkstra算法的java实现

import java.util.ArrayList;
import java.util.Scanner;


public class Main 
{   
    static ArrayList<Edge> list[];
    static int d[];//存储某个点到所有点的距离,若两个点不联通默认为最大值
    static int mark[];//访问标记
    static int n;//n个点
    static int m;//m条边
    public static void main(String[] args) 
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        m=sc.nextInt();
        int x=sc.nextInt();//起点
        d=new int[n+1];
        mark=new int[n+1];
        list=(ArrayList<Edge>[])new ArrayList[n+1];
        for(int i=0;i<m;i++)
        {
            int x=sc.nextInt();
            int y=sc.nextInt();
            int w=sc.nextInt();
            if(list[x]==null)list[x]=new ArrayList<>();
            if(list[y]==null)list[y]=new ArrayList<>();
            list[x].add(new Edge(y,w));
            list[y].add(new Edge(x,w));//图的构建,假设为无向图
        }
        Dijkstra(x);
        for(int i=1;i<=n;i++)
        {
            if(d[i]!=Integer.MAX_VALUE)//输出所有可达路径的最短路
            {
                System.out.println(x+"->"+i+":"+d[i]);
            }
        }   
    }
    void Dijkstra(int x)//求从x这个点出发,到所有点的最短路
    {
        for(int i=1;i<=n;i++)
        {
            d[i]=Integer.MAX_VALUE;//初始化,所有点均为最大值
        }
        d[x]=0;//定义起点到起点的距离为零
        for(int i=1;i<=n;i++)
        {
            int now=0,min=Integer.MAX_VALUE;
            for(int j=1;j<=n;j++)
                if(mark[j]!=1&&d[j]<min)min=d[now=j];//寻找之前没有考虑过且到原点距离最小的点,显然当第一次进入循环,必定是从x开始考虑
            mark[now]=1;//此时标记
            for(int j=0;j<list[now].size();j++)//找到所有与now邻接的边
            {
                int to=list[now].get(j).to;
                d[to]=Math.min(d[to], d[now]+list[now].get(j).w);//一直在更新最小值
            }
        }
    }
}
class Edge
{
    int to;//连到哪个边
    int w;//边的权值
    Edge(int to,int w)
    {
        this.to=to;
        this.w=w;
    }
}

如果题目还要求 最短路的路径,那么我们需要一个栈,假设从x到j点,
从尾端j开始考虑,用的d[j]=d[k]+w(j,k)(k为其中一个与j邻接的店)来判断k是否为j的上一个路径的点,当然这是针对无向图来说,有向图不适用
下面给出代码

static void findLast(int x,int begin)//递归调用
    {
        if(begin==x)//如果当前点和起点相同就结束
        {
            stack.push(x);
            return;
        }
        for(int i=0;i<list[x].size();i++)
        {
            int k=list[x].get(i).to;
            int w=list[x].get(i).w;
            if(d[x]==d[k]+w)//判断式
            {
                stack.push(x);
                findLast(k,begin);
                return;//这个是必要的,因为最短路的个数可能不只一个,即值相同,路径不相同,如果题目说明只有一个最短路,这个return可不加,
            }
        }
    }

或者 之前的代码

d[to]=Math.min(d[to], d[now]+list[now].get(j).w);

替代为

if(d[to] > d[now] + list[now].get(j).w) 
{
    d[to] = d[now] + list[now].get(j).w;
    pre[to] = now;//设置一个数组pre(previous)来记录上一个节点
}

这段代码的官方名称叫松弛操作(relaxation)。
优化:

 for(int i=1;i<=n;i++)
 {
     int now=0,min=Integer.MAX_VALUE;
     for(int j=1;j<=n;j++)
        if(mark[j]!=1&&d[j]<min)min=d[now=j];
     mark[now]=1;
     for(int j=0;j<list[now].size();j++)
     {
        int to=list[now].get(j).to;
        d[to]=Math.min(d[to], d[now]+list[now].get(j).w);
     }
  }

以上这段代码其实可以进一步优化一下,我们采用优先队列,来实现找出之前没有考虑过且到原点距离最小的点。为了使用队列,我们还需定义一个数据结构Pair(记录当前点的暂时的最短路)如下:

PriorityQueue<Pair> queue=new PriorityQueue<>();
        queue.add(new Pair(x,0));//加入第一个点
        while(!queue.isEmpty())
        {

            Pair p=queue.poll();//出队列
            if(mark[p.x]==1)continue;//如果之前考虑过就不再考虑
            mark[p.x]=1;//然后标志当前
            for(int j=0;j<list[p.x].size();j++)
            {
                int to=list[p.x].get(j).to;//其中一个与p.x邻接的店
                d[to]=Math.min(d[to], d[p.x]+list[p.x].get(j).w);
                    queue.add(new Pair(to,d[to]));
            }
        }
class Pair implements Comparable<Pair>
{
    int x;
    int curMin;
    Pair(int x,int CurMin)
    {
        this.x=x;
        this.curMin=curMin;
    }
    @Override
    public int compareTo(Pair k) 
    {
        if(curMin>k.curMin)return 1;
        if(curMin<k.curMin)return -1;
        // TODO Auto-generated method stub
        return 0;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值