单源最短路径-java解法

单源最短路径

此版本为Java题解版

单源最短路径-java解法_第1张图片

这是一道模板题,对于单源最短路径的题,我们首选Dijikstra算法。

首先我们对算法思路进行一个复习。

单源最短路径-java解法_第2张图片

暴力解法(思路清晰,适合入门)

  • 建立dis数组,用于记录单源最短路径。

  • 对于两部分的点,我们可以通过建立一个book数组进行标记,进行判断是否是最短路径确定的点。

  • 对于图的结构我们可以创建一个Edge类,单独进行储存。

  • 每次选出一个单源路径最短的点,进行松弛(更新dis数组的值),每个点均要进行一轮松弛,故需要进行n(顶点数)轮循环。

完整代码:

import java.util.Scanner;
class Edge{
    int point;
    int to;
    int weight;
}
class Main{
    public static void main(String args[]){
        Scanner scanner = new Scanner(System.in);
        int INF = Integer.MAX_VALUE;
        int sign = 0;
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int s = scanner.nextInt();
        int[] dis = new int[n+1];
        int[] book = new int[n+1];
        Edge[] edge = new Edge[m+1];
        for(int i = 0;i < m;i++){
            edge[i] = new Edge();
            edge[i].point = scanner.nextInt();
            edge[i].to = scanner.nextInt();
            edge[i].weight = scanner.nextInt();
        }
        //初始化dis数组
        for(int i = 0;i < m;i++){
            if(edge[i].point == s){
                dis[edge[i].to] = edge[i].weight;
            }
        }
        for(int i = 0;i <= n;i++){
            if(dis[i] == 0){
                dis[i] = INF;
            }
        }
        dis[s] = 0;
        //Dijisktra核心算法
        for(int i = 1;i <= n;i++){
            int min = INF;
            for(int j = 1;j <= n;j++){
                if(book[j] == 0 && dis[j] < min){
                    min = dis[j];
                    sign = j;
                }
            }
            book[sign] = 1;
            for(int k = 0;k < m;k++){
                if(edge[k].point == sign){
                    if(dis[edge[k].to] > dis[sign] + edge[k].weight){
                        dis[edge[k].to] = dis[sign] + edge[k].weight;
                    }
                }
            }
        }
        for(int i = 1;i < dis.length;i++){
            System.out.print(dis[i]+" ");
        }
    }
}

该算法的复杂度为:n²

使用优先队列与邻接表进行优化

对于上述暴力解法的过程,我们可以总结出几个可以优化的过程。

  • 每次选出最短路径点的时候,我们可以先用优先队列进行储存,直接将堆顶元素弹出。
  • 可以不用遍历所有边,使用邻接表储存边,直接遍历当前点为起点的边。

优先队列(Java集合框架【Priorityqueue】)的使用方法

此题中我们创建node结点,作为存入框架的对象。

//用于入堆的结点
class node{
    int index;
    int value;
}
static PriorityQueue<node> min;
min = new PriorityQueue<>(new Comparator<node>(){
            public int compare(node a,node b){
                return a.value - b.value;
            }
        });
/*使用方法
peek() 访问队头元素
element() 访问队头元素
empty() 队列是否为空
offer() 插入元素到队尾 (并排序)
poll() 弹出队头元素
*/

其中index代表当前结点,value代表dis[index](当前结点最短路径)。

为了能使优先队列正常工作,我们需要在一个Comparator接口进行不同结点的比较。(注意格式),该对象的泛型为你要存入优先队列的自定义数据类型。然后在该Comparator接口lambda表达式中实现compare方法。

最小堆返回上述代码,最大堆相反。

邻接表的建立

class Edge{
    int to;
    int weight;
    int next;
}

Edge的next元素代表着下一个以该节点为起点的边。

  int[] head = new int[n+1];
        Edge[] edge = new Edge[m+1];
        //注意此处从1开始
        for(int i = 1;i < m+1;i++){
            edge[i] = new Edge();
            int index = scanner.nextInt();
            edge[i].to = scanner.nextInt();
            edge[i].weight = scanner.nextInt();
            edge[i].next = head[index];
            head[index] = i;
        }

完整解答代码

import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;
class Edge{
    int to;
    int weight;
    int next;
}
//优先队列结点
class node{
    int index;
    int value;
}
public class Solution{
    static PriorityQueue<node> min;
    public static void main(String args[]){
        Scanner scanner = new Scanner(System.in);
        int INF = Integer.MAX_VALUE;
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int s = scanner.nextInt();
        int[] dis = new int[n+1];
        int[] book = new int[n+1];
        int[] head = new int[n+1];
        Edge[] edge = new Edge[m+1];
        //注意此处从1开始
        for(int i = 1;i < m+1;i++){
            edge[i] = new Edge();
            int index = scanner.nextInt();
            edge[i].to = scanner.nextInt();
            edge[i].weight = scanner.nextInt();
            edge[i].next = head[index];
            head[index] = i;
        }
        //初始化dis数组
        for(int i = 0;i < dis.length;i++){
            dis[i] = INF;
        }
        dis[s] = 0;
        min = new PriorityQueue<>(new Comparator<node>(){
            public int compare(node a,node b){
                return a.value - b.value;
            }
        });
        node n1 = new node();
        n1.index = s;
        n1.value = 0;
        min.offer(n1);
        while(!min.isEmpty()){
            node temp = min.peek();
            min.poll();
            int index = temp.index;
            int value = temp.value;
            //System.out.println(index);
            //System.out.println(value);
            if(book[index] != 0) continue; 
            book[index] = 1;
            for(int i = head[index];i!=0;i = edge[i].next){
                int to = edge[i].to;
                if(dis[to] > dis[index] + edge[i].weight){
                    dis[to] = dis[index] + edge[i].weight;
                    if(book[to] != 1){
                    //System.out.println("--------0--------");
                    node n2 = new node();
        			n2.index = edge[i].to;
        			n2.value = dis[to];
        			//System.out.println(index);
        			//System.out.println(value);
        			//System.out.println("--------0--------");
                    min.offer(n2);
                    }
                }
            }
        }
        for(int i = 1;i < dis.length;i++){
            System.out.print(dis[i]+" ");
        }
    }
}

这样过了一遍是不是感觉Dijikstra也没那么难了。

特别提醒

Java程序相较c++程序运行内存占用大,在进行程序设计竞赛且测试点要求较高时,不推荐使用。

单源最短路径-java解法_第3张图片

Memory Limit Exceeded.


2021.8.31

你可能感兴趣的:(洛谷题解,算法笔记,java,算法,数据结构)