问题描述
给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。
输入格式
第一行两个整数n, m。
接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。
输出格式
共n-1行,第i行表示1号点到i+1号点的最短路。
解题代码(java)
相关解题思路见代码注释
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
static List<List<Integer>> adjoinList=new ArrayList<>();//邻接表
static int[] pointUse=new int[40001];//标定结点是否被遍历
static int[] brotherIndex=new int[40001];//标定兄弟结点的下标,初始为0,使用时相当于从最后一个兄弟结点向前遍历
static int[] result=new int[40001];//结果列表
static List<Integer> queen=new ArrayList<>();//标定队列中的点
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
String[] data=scanner.nextLine().split(" ");
int n=Integer.parseInt(data[0]);
int m=Integer.parseInt(data[1]);
for(int i=0;i<=n;i++){
brotherIndex[i]=-1;
}
for(int i=0;i<m;i++){
List<Integer> list=new ArrayList<>();
String[] dataM=scanner.nextLine().split(" ");
int row=Integer.parseInt(dataM[0]);
int line=Integer.parseInt(dataM[1]);
int value=Integer.parseInt(dataM[2]);
list.add(row);//第一个结点
list.add(line);//第二个结点
list.add(value);//边权值
list.add(brotherIndex[row]);//置row作为父节点,line作为子节点时,line的兄弟结点
brotherIndex[row]=i;//目的是使下一个以row为第一个结点的边可找到line的上一个兄弟结点
adjoinList.add(list);
}
SDFS(1);
for(int i=2;i<=n;i++){
System.out.println(result[i]);
}
}
//从第一个结点接入,遍历其所有子节点(brotherIndex[i]中最终存储的是第i个结点下的最后一个子节点,
// 并且在adjoinList中,存储有i的上一个子节点的List下标值),在遍历子节点过程中进行入队列,出队列的操作
//当result[j]更新时,j结点入队列,因为此点更新的是局部点,需要入队列以后出队列重新计算与前面点距离加和
//并且在队列中的点不能被遍历到,因为队列中的点要么是待更新的点,要么就是局部更新的点,如果让其他点可以遍历,就会造成数据总和的错误
//说明出队列的点一定是result[j]到达1结点的全局数据,可以让其他点遍历使用(同时也是为了防止重复遍历,因为遍历会带来数据的改变)
//知道队列为空时,说明结果数组中的各个值到1点的距离为最短路径
public static void SDFS(int n){
//初始化队列列表
queen.add(n);
//标定当前点已经被遍历过
pointUse[n]=1;
while (queen.size()>0){
//利用brotherIndex获取当前结点子节点的最后一个兄弟结点,利用adjoinList更新i值,达到遍历当前结点的所有兄弟结点的目的
int index=queen.get(0);
queen.remove(0);
pointUse[index]=0;//从队列中取出则证明可以遍历
for(int i=brotherIndex[index];i!=-1;i=adjoinList.get(i).get(3)){
//判断是否可以更新
int v=adjoinList.get(i).get(1);//获取子节点
int u=index;
int value=adjoinList.get(i).get(2);//获取结点值
int tempValue=result[u]+value;
//如果当前结点没有被遍历过并且结果数组中的值没有被遍历过或者结果数组中的值大于其父节点+父节点到此节点的边权值时->更新
if(pointUse[v]!=1&&(result[v]==0||result[v]>tempValue)){
result[v]=tempValue;
pointUse[v]=1;//证明已经被遍历
queen.add(v);//队列中存储的是结点
}
}
}
}
}