单源最短路径
此版本为Java题解版
这是一道模板题,对于单源最短路径的题,我们首选Dijikstra算法。
首先我们对算法思路进行一个复习。
暴力解法(思路清晰,适合入门)
-
建立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++程序运行内存占用大,在进行程序设计竞赛且测试点要求较高时,不推荐使用。
Memory Limit Exceeded.