图论记录之最短路迪杰斯特拉

简述思想

这个思想能用一句话来概括,精简到的极致:每次找到一个最短距离的点并更新起点到各个点的最短距离
如果要可视化的话,B站搜索Dijksra算法,有视频讲解

伪代码

写到这里,其实是想整一个动画的,这样效果更好点,但由于种种原因所以就拖一下

int dijkstr()
{
	dist[1] = 0;
	其余的点的距离全部初始化为真无穷,不要写成int的最大值
	迭代n次
		将不在s中的,且距离最近的点给t
		s<-t
	用t更新其他点的距离
}

代码

这里是Acwing的851题,下面的有注释

import java.util.*;

public class Main
{
    private static int N =510;
    private static int n,m;
    private static int[] dist;//存放起点到每个点的最短距离
    private static int[][] g;//邻接矩阵
    private static boolean[] st;//若为true表示已经确定了起点到i点的最短距离
    static Scanner in = new Scanner(System.in);
    static int dijkstra()
    {
       Arrays.fill(dist,100001);
       dist[1]=0;//从起点到起点是0,这个很好理解
       // 迭代n次
       for(int i=0;i<n;i++){
           int t=-1;
           for(int j=1;j<=n;j++){
           		/*
				!st[j]表明j这个点我还没有访问
				t==-1 表明还在初始状态,初始状态必定进入该if分支
				dist[j]
               if(!st[j] &&(t==-1 || dist[j]<dist[t]))
                t=j;
           }
           st[t]=true;//标记节点t为访问状态
           for(int j=1;j<=n;j++)
           // 1~t t->j 即先到t,再加上t到j这一段距离,也叫做最后一段距离
            dist[j]=Math.min(dist[j],dist[t]+g[t][j]);
       }
       // 能进入该分支,表明再迭代n次后,没有任何一个点能到达终点n,所以终点不可达,那么返回-1(题目要求的)
       if(dist[n]==100001)
            return -1;
        return dist[n];
    }

    public static void main(String[] args)
    {

        n = in.nextInt();
        m = in.nextInt();
        g = new int[n+1][n+1];
        dist = new int[n+1];
        st=new boolean[n+1];
        for(int[] arr:g)
            //不要写成Integer.MAX_VALUE,由于dist[t]+g[t][j],这个运算操作会溢出
            Arrays.fill(arr,100001);
        while(m-->0){
            int x = in.nextInt();
            int y = in.nextInt();
            int z = in.nextInt();
            //重复边取最小
            g[x][y] = Math.min(g[x][y],z);
        }
        System.out.println(dijkstra());
    }

}

堆优化

其实这个优化是对下面这个循环进行优化,这个循环是用t去更新所有从起点到j的距离,会有不需要更新的点,我们是否可以避免哪些不需要更新的操作?这就是堆干的事情了

for(int j=1;j<=n;j++)
         // 1~t t->j 即先到t,再加上t到j这一段距离,也叫做最后一段距离
         dist[j]=Math.min(dist[j],dist[t]+g[t][j]);

// 就比如说10 15,堆中弹出来的可能是之前已经确定的最短距离的点,这个时候st数组的作用就发挥出来了,直接丢掉

import java.util.*;

public class Main
{
    private static int N =150010;
    private static int n,m;
    private static int[] dist;//存放起点到每个点的最短距离
    private static int[] h;//邻接表
    private static int[] ne = new int[N];//指针域 100010 的原因是最多右10000条边,多开10个无伤大雅,因为这样避免出现边界问题

    private static int[] e = new int[N];//数据域
    private static int[] weight = new int[N];//权值
    private static boolean[] st;//若为true表示已经确定了起点到i点的最短距离
    private static int idx =0;
    static Scanner in = new Scanner(System.in);
    static int dijkstra()
    {
        Arrays.fill(dist,1000000000);
        dist[1]=0;//起点离自己的距离是0
        PriorityQueue<int[]> pq = new PriorityQueue<>(n,(x,y)->{return x[1]-y[1];});
        pq.offer(new int[]{1,0});
        while (!pq.isEmpty()){
            int[] poll = pq.poll();
            int t = poll[0],distance = poll[1];
            if(st[t])continue;
            st[t]=true;
            for(int i=h[t];i!=-1;i=ne[i]){
                int vertex = e[i];
                if(dist[vertex] >distance+weight[i]){
                    dist[vertex] = distance+weight[i];
                    pq.offer(new int[]{vertex,dist[vertex]});
                }
            }
        }
        if(dist[n]==1000000000)
            return -1;
        return dist[n];

    }
    static void add(int x,int y,int z){
        e[idx]=y;
        ne[idx]=h[x];
        weight[idx]=z;
        h[x]=idx++;
    }
    public static void main(String[] args)
    {

        n = in.nextInt();
        m = in.nextInt();
        h = new int[n+1];
        dist = new int[n+1];
        st=new boolean[n+1];
        Arrays.fill(dist,1000000000);
        Arrays.fill(h,-1);
        Arrays.fill(ne,-1);
        while(m-->0){
            int x = in.nextInt();
            int y = in.nextInt();
            int z = in.nextInt();
            add(x,y,z);
        }
        System.out.println(dijkstra());
    }
}

你可能感兴趣的:(算法,图论,java,开发语言)