一、Dijkstra算法
1.算法思想
该算法的思想是先分成两大阵营——已找到最短路径的顶点阵营a和还未找到最短路径的顶点阵营b。即要进行n-1次循环(n为顶点数,-1是因为不包括源点),每一次循环都要去找到距离源点距离最近的并且未标记的一个点A,然后把他加进阵营a中,并记录它到源点的距离为c;接着开始更新距离,即:去找A顶点的邻接顶点m,假设Am距离为distance,判断这个顶点m是不是属于阵营b,如果是的话,就去判断distance+c是不是小于原来这个顶点m到源点的距离,如果是的话就更新距离;反复执行这个过程n-1次即可
详情请看:
https://blog.youkuaiyun.com/qq_38410730/article/details/79587768
2.算法步骤
(1) 先要设置三个数组
①布尔型st数组判断是属于哪个阵营
②整型dist数组,记录各顶点到源点的距离
③path数组,记录最短距离路径
(2)然后对数组进行初始化
st数组除了源点,全部设为false,dist源点设为0,其余都是Max,path为源点
(3)开始找最短路径
两大阵营——已找到最短路径的顶点阵营a和还未找到最短路径的顶点阵营b。即要进行n-1次循环(n为顶点数,-1是因为不包括源点),每一次循环都要去找到距离源点距离最近的并且未标记的一个点A,然后把他加进阵营a中,并记录它到源点的距离为c;接着开始更新距离,即:去找A顶点的邻接顶点m,假设Am距离为distance,判断这个顶点m是不是属于阵营b,如果是的话,就去判断distance+c是不是小于原来这个顶点m到源点的距离,如果是的话就更新距离;反复执行这个过程n-1次即可
(4)打印最短路径
对于源点能到的每个顶点,进行打印Path路径
3.实现过程
(1)构建图的存储模板
以后有出现图的,都用邻接表去存,即用一个arraylist作为table数组即顶点标识,LinkedList作为下一个下一个点的信息。一共就分为三个类,第一类是Vertex类,包括了endVertex和权重等顶点信息,是用来给LinkedList铺垫的;第二类是Edge类,这个类就只需要构造LinkedList链表即可;第三类是Graph类,这个类要包含arraylist的作为顶点标识,然后还要有一个buildgraph的函数
(2)执行Dijk算法
(3)求出结果并打印出源点到各个顶点的最短路径和距离
4.实现代码
输入格式:第一行有两个整型分别为顶点数和边数N,接下来的N行有三个数分别为顶点,终点和权重
输出:源点到各个顶点的最短路径和距离
import java.lang.*;
import java.util.*;
//无向图的最短路径
class Vertex{
public int endVertex;
public int weight;
Vertex(int a,int b){
this.endVertex=a;
this.weight=b;
}
}
class Edge{
public int noble;
public List<Vertex>adj;
Edge(int a){
this.noble=a;
this.adj=new LinkedList<>();
}
}
class Graph{
private List<Edge> Vertexs;
private int numberpfvertexs;
private int numberofedges;
Graph(int a,int b){
this.numberpfvertexs=a;
this.numberofedges=b;
Vertexs=new ArrayList<>(a+1);
}
void buildGraph(){
Scanner cin=new Scanner(System.in);
for(int i=0;i<=numberpfvertexs;i++){
Edge flag=new Edge(i);
Vertexs.add(i,flag);//先将列表的每一个元素开辟一个空间,然后只用到这个空间的LinkedList部分
}
for(int i=0;i<numberofedges;i++){
//读取信息
int start=cin.nextInt();
int end=cin.nextInt();
int weight=cin.nextInt();
//设置起点与终点结点
Vertex Start=new Vertex(end,weight);
Vertex End=new Vertex(start,weight);
//获取列表中的对应下标的元素
Edge startNode=Vertexs.get(start);
Edge endNode=Vertexs.get(end);
//改变该对点起点的LinkedList的adj,即只用到一个结点的LinkedList部分
startNode.adj.add(Start);
Vertexs.set(start,startNode);
//改变该对点终点的LinkedList的adj,即只用到一个结点的LinkedList部分
endNode.adj.add(End);
Vertexs.set(end,endNode);
}
}
Edge getlist(int v){
return this.Vertexs.get(v);
}
}
public class Dijkstra {
void dijkstra(Graph graph,int number,int v){
int []dist=new int[number+1];
boolean []st=new boolean[number+1];
int []path=new int[number+1];
int max=1000000000;
//初始化
for(int i=0;i<=number;i++){
dist[i]=max;//如果源点直接没有直接相连的话,就是最大值距离
st[i]=false;
}
Edge nextVertex= graph.getlist(v);
List <Vertex> NextVertex;
NextVertex=nextVertex.adj;
for(Vertex i:NextVertex){
System.out.println(i.endVertex+" "+i.weight);
dist[i.endVertex]=i.weight;//如果源点直接相连的话就直接等于权重
}
st[v]=true;path[v]=v;
for(int i=0;i<=number;i++){
System.out.print(dist[i]+" ");
}
System.out.println();
//开始寻找最短路径
int fisrt=v;
for(int i=2;i<=number;i++)//除去源点之后,一共还剩下number-1个顶点
{
int min=max; int loc=0;
for(int j=1;j<=number;j++){
if(st[j]==false&&dist[j]<min){
min=dist[j];
loc=j;
}
}
if (loc!=0) st[loc]=true;
path[loc]=fisrt;
fisrt=loc;
Edge nextvertex=graph.getlist(loc);
List<Vertex> LocNext=nextvertex.adj;//找到该顶点的下一个邻接边
//更新边,即松弛
for(Vertex h:LocNext){
if(st[h.endVertex]==false&&(min+h.weight<dist[h.endVertex])){
dist[h.endVertex]=min+h.weight;//松弛成功
}
}
for(int c=0;c<=number;c++){
System.out.print(dist[c]+" ");
}
System.out.println();
}
//打印最短路径过程与距离
for(int i=1;i<=number;i++){
int record=i;
if(i==v) System.out.println(v+" "+dist[i]);
else{
while(record!=v){
System.out.print(record+" ");
record=path[record];
}
System.out.println(v+" "+dist[i]);
}
}
}
public static void main(String []args){
int vertexs,edges;
Scanner cin=new Scanner(System.in);
vertexs=cin.nextInt();edges=cin.nextInt();
Graph graph=new Graph(vertexs,edges);
graph.buildGraph();
Dijkstra K=new Dijkstra();
System.out.print("请输入源顶点:");
int v=cin.nextInt();
K.dijkstra(graph,vertexs,v);
}
}
5.出错点与注意点
①注意点:要特别注意这个图的存储形式是如何遍历各个顶点的邻接顶点的信息的,用foreach法
②易错点:还是List问题,我们知道,在构造一个list之后,要对其进行一个遍历,撑开它的空间,因此,一开始我遍历的时候是
Vertexs=new ArrayList<>(a+1);
Edge flag=new Edge(-1);
for(int i=0;i<=numberpfvertexs;i++){
Vertexs.add(i,flag);//先将列表的每一个元素开辟一个空间,然后只用到这个空间的LinkedList部分
}
我这个new是放在循环外了,这个是错误的,因为如果是在循环外就已经先new好了一个变量的话,那么在循环内虽然是一直在add,但它一直都是指向同一个位置。因为各种集合就像一个容器,它们只是选中变量的
那个单元,然后将其映射到容器里面,因此会出现如下情况:
所以以后new空间都要在循环内new
---------------------------------------------3.27 更新--------------------------------------------
package Knowledgement;
import java.util.Arrays;
import java.util.Scanner;
/*
* Floyd算法求最短路径适合于图的节点个数不超过200个,且用邻接矩阵存储的
*/
public class _最短路径Floyd {
static int numbernode=200;
static int [][]dis=new int [numbernode][numbernode];//dis存储的是各点的最短距离,一开始存储的为各点的权重,初始化的是INF
static int INF=100000000;
static void Floyd() {
for(int k=0;k<numbernode;k++) {
for(int i=0;i<numbernode;i++) {
for(int j=0;j<numbernode;j++) {
if(dis[i][k]!=INF&&dis[k][j]!=INF&&(dis[i][k]+dis[k][j]<dis[i][j])) {
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
}
}
public static void main(String []args) {
Scanner cin=new Scanner(System.in);
Arrays.fill(dis,INF);
numbernode=cin.nextInt();
int edgenumber=cin.nextInt();
for(int i=0;i<numbernode;i++) {
dis[i][i]=0;//初始化为0
}
for(int j=0;j<edgenumber;j++) {
int a=cin.nextInt();
int b=cin.nextInt();
dis[a][b]=dis[b][a]=cin.nextInt();
}
Floyd();
for(int i=0;i<numbernode;i++) {
for(int j=0;j<numbernode;j++) {
System.out.print(dis[i][j]);
}
System.out.println();
}
}
}