讲解改天有空了再写
完整代码如下:
public class Dijkstra {
public static void main(String[] args) {
char[] vertex = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
//邻接矩阵
int[][] matrix = new int[vertex.length][vertex.length];
final int N = 1000;// 表示不可以连接
matrix[0]=new int[]{N,5,7,N,N,N,2};
matrix[1]=new int[]{5,N,N,9,N,N,3};
matrix[2]=new int[]{7,N,N,N,8,N,N};
matrix[3]=new int[]{N,9,N,N,N,4,N};
matrix[4]=new int[]{N,N,8,N,N,5,4};
matrix[5]=new int[]{N,N,N,4,5,N,6};
matrix[6]=new int[]{2,3,N,N,4,6,N};
Graph_Di graph_di = new Graph_Di(7);
graph_di.vertexes = vertex;
graph_di.edges =matrix;
graph_di.dijkstra(0);
System.out.println();
}
}
class Graph_Di {
int N;//顶点数
char[] vertexes;
int[][] edges;
int[] minDis;
int[] check;
int[] preVertex;
public Graph_Di(int nums) {
N = nums;
vertexes = new char[nums];
edges = new int[nums][nums];
minDis = new int[vertexes.length];
check = new int[vertexes.length];
preVertex = new int[vertexes.length];
}
/**
* 迪杰斯特拉算法入口方法
* @param start
*/
public void dijkstra(int start) {
//构建用到的三张表,最小值表、前驱表
// 手动生成preVertex表
Arrays.fill(preVertex, start);
// 手动横撑距离表
Arrays.fill(minDis, 1000);
minDis[start] = 0;
//老韩的需要先手动调一下,我的不用,我的实现直接循环就可以了。
//先手动调用一下scan,将minDis生成一下。
// scan(start);
while (true){
int minDisSub = getMinDisSub();
if(minDisSub != -1){
scan(minDisSub);
}else break;
}
}
/**
* 调用scan的方法,即不断从最小值表中得到最小值,然后放入scan方法中
* 如果没有最小值,或者已经全部遍历过了,返回-1;
*
* 实际上可以用优先队列来实现这个结构,毕竟该函数目的就是为了弹出一个最小值罢了
*/
public int getMinDisSub(){
int minDistance = 1000;
int sub = -1;
for (int i = 0; i < minDis.length; i++) {
if(check[i] == 0 && minDis[i] < minDistance){
minDistance = minDis[i];
sub = i;
}
}
return sub;
}
/**
* 扫描sub结点,找出所有与sub结点连接的点,按照要求对该点进行判断,判断成功则更新该点minDis,失败则跳过
* 如果判断成功,将该点距离加入最小指标,修改该点痕迹表,以及为该点重新设置前驱表,
* 如果判断失败,跳过修改步骤,结束方法
* 判断方法的内容为:
* 1.设与sub点连接的点为v,v距离v0的距离是否小于最小值表中v距离v0的距离。具体如下:
* sub距离v的最小距离为sub-v,v0到sub的最小距离为minDis[sub],v0到v的最小距离(原)为minDis[v],
* 判断(sub-v)+minDis[sub] < minDis[v],是否成立,如果成立,即判断成功,如果不成立,即判断失败。
*
* @param sub
*/
public void scan(int sub) {
//能够进入这个方法,就说明该点已经是目前最短的点了,因此把该点的check设为1也是正常的。
// 不管后续会不会有修改,这是第一次确定该点为最短的时候,如果后续有修改,那是后续的事情了,与该点是否到达无关
check[sub] = 1;
for (int i = 0; i < edges.length; i++) {
//先把所有和sub点相邻的点的左边放进去判断一下,为了节约时间,只和没走过的点判断
if(check[i] == 0){
//进入判断方法
minDisUpdate(sub, i, edges[sub][i]);
}
}
}
/**
* 判断方法,具体描述写在了scan方法上面
* @param startSub 源点
* @param minWeightSub 终点
* @param weight 两点间的权值
*/
private void minDisUpdate(int startSub, int minWeightSub, int weight) {
//如果小于原值,那么就更新值,并且更新前驱表
//和原来的值比较,如果大于原来的值,就跳过了
if (minDis[minWeightSub] > weight + minDis[startSub]) {
minDis[minWeightSub] = weight + minDis[startSub];
preVertex[minWeightSub] = startSub;
}
}
}
acwing上面实现的dijkstra算法,算是个模板,我修改了一点,可读性高了点,容易背:
import java.util.*;
/**
* @desc 使用acwing(oj)用的模板
*/
public class Main {
//定义容器、常数、读入
int N = 510;
int M = 100010;
int[][] g = new int[N][N];//邻接矩阵
boolean st[] = new boolean[N];
int[] dis = new int[N];
Scanner jin = new Scanner(System.in);
int n,m;
//oj要用的main方法
public static void main(String[] args) {new Main().run();}//在oj中调用题解方法
void run() {
//读入题目数据
n = jin.nextInt();
m = jin.nextInt();
//dis数组、邻接表预处理
for(int i = 1; i <= n; i++){
dis[i] = M;
for(int j = 1; j <= n; j++){
g[i][j] = M;//无穷
}
}
for (int i = 0; i < m; i++) {//麻了,这里竟然有bug,输入没搞明白。。草
int a = jin.nextInt();
int b = jin.nextInt();
int c = jin.nextInt();
g[a][b] = Math.min(g[a][b],c);//去除重边
}
//调用解题方法
int ans = dijkstra();
//输出题解答案
System.out.print(ans);
}
int dijkstra(){
// Arrays.fill(dis, 0x3f3f);
dis[1] = 0;
for(int i = 0; i < n; i++){//从1开始探索寻找最短边
//寻找S集合中的最短点,设t点为最小点
boolean flag = false;//是否寻找到了点集中的一个点
int t = -1;
for(int j = 1; j <= n; j++ ){//每次探索都要从1开始,直到n结束
if(!st[j]){//如果符合要求
if(!flag) {
t = j;
flag = true;
}
if(flag && (dis[t] > dis[j])) t = j;
}
}
st[t] = true;//必定有一个t是在这里的,这是dijkstra算法的理论,不需要自己证明
for(int j = 1; j <= n; j++){//从t出发,寻找dis[t]+g[t][j]最小值;更新dis表
dis[j] = Math.min(dis[j], dis[t] + g[t][j]);
}
}
if(dis[n] == M) return -1;
else return dis[n];
}
}