2020/11/5迪杰斯特拉算法的堆优化

题目

给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为非负值。

请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出-1。

输入格式
第一行包含整数n和m。

接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。

输出格式
输出一个整数,表示1号点到n号点的最短距离。

如果路径不存在,则输出-1。

数据范围
1≤n,m≤1.5×105,
图中涉及边长均不小于0,且不超过10000。

输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3

解法分析

首先我们要知道,这个算法只不过是朴素迪杰斯特拉算法的过程的优化,其核心思路其实是不变的.
首先回忆朴素迪杰斯特拉算法:

  1. 初始化
  2. 遍历,找到最小的距离,标记(加入确认集合)
  3. 遍历,更新所有的距离
  4. 循环2,3 直到所有点
    堆优化的算法就是将这两次遍历优化:
    1.初始化堆,初始化链表
    2.弹出堆头,标记(加入确认集合)
    3.找到堆头相邻的结点,并对其更新,如果结点更新了,就加入堆中,直到堆空
    可以看到,找最小距离的步骤我们用弹出堆头的操作来简化;而更新节点的过程,我们用找邻节点的方式简化了遍历的过程
    因此,本题我们要用链表来存储图,而链表适用于稀疏图,所以我们说,稀疏图我们要用堆优化的迪杰斯特拉算法来做

源代码

import java.util.*;
class Main{
    static int n;
    static int N=150010;
    static int INF=0x3f3f3f3f;
    static int index=0;
    static int[] h=new int[N];
    static int[] e=new int[N];
    static int[] ne=new int[N];
    static boolean[] st=new boolean[N];
    static int[] dis=new int[N];
    static int[] w=new int[N];
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        int m=sc.nextInt();
        while(m-->0){
            int x=sc.nextInt();
            int y=sc.nextInt();
            int z=sc.nextInt();
            add(x,y,z);
        }
        Arrays.fill(dis,INF);
       System.out.print(djs_2());
        
    }
    public static int djs_2(){
        PriorityQueue<Pair> pq=new PriorityQueue<Pair>();
        pq.add(new Pair(0,1));
        dis[1]=0;
        while(!pq.isEmpty()){
            Pair p=pq.poll();
            int d=p.getDistance();
            int n=p.getNumber();
            if(st[n]) continue; //这句的作用是什么呢?如果有一模一样的两条重边,第二个就不用在根据节点往下找了(找了浪费时间而已,因为下边的距离已经更新过了,所以即使往下走也不会再更新了)
            st[n]=true;
            for(int i=h[n];i!=0;i=ne[i]){
                int j=e[i];
                if(dis[j]>dis[n]+w[i]) {//这里的dis[n]其实就是d
                    dis[j]=dis[n]+w[i];
                    pq.add(new Pair(dis[j],j));
            }
        }
        }
        if(dis[n]==INF) return -1;
        return dis[n];
    }
    public static void add(int x,int y,int z){
        e[++index]=y;
        ne[index]=h[x];
        h[x]=index;
        w[index]=z;//为什么用index来存边的权重呢? 因为1个index就对应一个新的边的产生(虽然e[index]表示的是节点,但是这个节点是可以重复的,只有index不能重复),因此用index是最适合的
    } //换言之给出了前节点,通过指针我们就能知道后面节点的所有信息
}
class Pair implements Comparable<Pair>{
    private int distance;
    private int number;
    public Pair(int distance,int number){
        this.distance=distance;
        this.number=number;
    }
    public int getDistance(){
        return this.distance;
    }
    public int getNumber(){
        return this.number;
    }
    public int compareTo(Pair o){
        return Integer.compare(this.distance,o.distance);
    }
}

写在最后

  • 最大值的选择一定要写:0x3f3f3f3f,否则(0x3f3f3f)不够大
  • 邻接表的index是很重要的,因为我们遍历的时候用的就是index,所以我们要得到的其他信息在定义时都要由index来定义/找到
  • 重写compareto的时候要注意两点:1. 要写实现了接口comparable 2.要使用泛型 comparable<某某> 否则,传入的参数只能是obj类型的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值