單源最短路徑及其java實現
阿新 • • 發佈:2019-02-03
演算法思想連結:演算法思想及c++實現
本文采用java實現,並帶有略微詳細的註解。
package com.qf.greaph;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* @author jiayoo
* 7 / 30
* Dijkstra最短路徑演算法是一種單源最短路徑
* 本文采用的是鄰接表表示圖。
*
* 圖的表示: 1. 採用 ArrayList 來儲存 圖的頂點
* 2. 採用 Map 來儲存 邊集 , map 可以 實現 一對多的關係, 因此能很好的實現鄰接表結構
* 3. 採用ArrayList的原因 是使 邊集有序 這樣, Node 的裡面 那個記錄距離的集合才能一一對應
*/
public class MinPath {
private static class graph{
private ArrayList<Node1> nodes = new ArrayList<>(); // 表示圖頂點 , 同時他也作為V集合
private Map<Node1, ArrayList<Node1>> adjaNode = new HashMap<>(); // 表示圖的邊
private ArrayList<Node1> nodes1 ; // 表示S集合, 即儲存已經訪問的節點,
private float[] minPath; //用來儲存源點到每個頂點的距離
float min = Float.MAX_VALUE;
/**
* @param start
* @param end
* @param distance
* 構建鄰接表。使之成為圖
*/
public void addAdjaNode(Node1 start, Node1 end, float distance) {
if (!nodes.contains(start)) {
nodes.add(start);
}
if (!nodes.contains(end)) {
nodes.add(end);
}
if (adjaNode.containsKey(start) && adjaNode.get(start).contains(end)) {
return ;
}
if (adjaNode.containsKey(start)) {
adjaNode.get(start).add(end);
}else {
ArrayList<Node1> node = new ArrayList<Node1>();
node.add(end);
adjaNode.put(start, node);
}
start.distonext.add(distance);
}
/**
* 將圖打印出來
*/
public void prinGraph() {
if (nodes == null || adjaNode == null) {
System.out.println("圖為空");
return ;
}
for (Entry<Node1, ArrayList<Node1>> entry : adjaNode.entrySet()) {
System.out.println("頂點 : " + entry.getKey().name + " 連結頂點有: ");
for(int i = 0; i < entry.getValue().size(); i++) {
System.out.print(entry.getValue().get(i).name + " " + "距離是: " + entry.getKey().distonext.get(i) + ", ");
}
System.out.println();
}
}
/**
* 1.這個方法用於初始化S集合 及 初始化距離陣列
* 2. 設定源點, 並且將源點作為內容 初始化演算法
*/
public void findMinPath() {
Node1 node1 = null; // 用來記錄列表裡最小的點
nodes1 = new ArrayList<>(); // 儲存已經遍歷過的點
minPath = new float[nodes.size()]; // 初始化距離陣列
int i;
/*
* 對最短路徑進行初始化, 設定源點到其他地方的值為無窮大
* */
for (i = 0; i < minPath.length; i++) {
minPath[i] = Float.MAX_VALUE;
}
Node1 node = nodes.get(0);
nodes1.add(node); // 將源點加入 S 集合
node.visited = true;
ArrayList<Node1> n = adjaNode.get(node); // 獲取到源點的邊集
/*
* 先對源節點進行初始化
* 1. 對 距離陣列進行初始化。
* 2. 找到源點到某個距離最短的點, 並標記
*
* */
for (i = 0; i < n.size(); i++) {
minPath[n.get(i).id] = node.distonext.get(i); // 最短路徑記錄
if (min > node.distonext.get(i)) {
min = node.distonext.get(i);
node1 = n.get(i); // 找到當前最短路徑
}
}
this.process(node1, min);
}
private void process(Node1 node, float distance ) {
min = Float.MAX_VALUE; //作為標記
Node1 node1 = null; // 同樣記錄距離最短的點
int i;
ArrayList<Node1> n = adjaNode.get(node); // 獲得邊集
for (i = 0 ; i < n.size(); i++) {
if (!n.get(i).visited) { // 這個邊集裡的頂點不在 S 集合裡
if (minPath[n.get(i).id] == Float.MAX_VALUE) {
minPath[n.get(i).id] = distance + node.distonext.get(i); // 源點到下一點的距離
}else if (distance + node.distonext.get(i) < minPath[n.get(i).id] ) { //源點到該頂點的距離變小了, 則改變
minPath[n.get(i).id] = distance + node.distonext.get(i); // 更新源點到下一個點的距離
}
}
}
/*
* 這個for 用於找到 距離集合中 距離源點最近 且並未被訪問過的
* 這個for 同時可以確保 該節點確實可到達
* */
for (i = 1; i < minPath.length; i++) {
if (!nodes.get(i).visited) {
if (min > minPath[i] ) {
min = minPath[i];
node1 = nodes.get(i);
}
}
}
if (node1 != null) {
node1.visited = true;
process(node1, min); //源點到 當前的距離
}else { // 說明此位置沒有後續節點, 或者 已經全部被訪問完了, 則到達此位置只需要加上此位置的值
}
}
}
public static void main(String[] args) {
Node1 n1 = new Node1(0,"A");
Node1 n2 = new Node1(1,"B");
Node1 n3 = new Node1(2,"C");
Node1 n4 = new Node1(3,"D");
Node1 n5 = new Node1(4,"E");
Node1 n6 = new Node1(5,"F");
graph gp = new graph();
gp.addAdjaNode(n1, n2, 6);
gp.addAdjaNode(n2, n1, 6);
gp.addAdjaNode(n1, n3, 3);
gp.addAdjaNode(n3, n1, 3);
gp.addAdjaNode(n2, n3, 2);
gp.addAdjaNode(n3, n2, 2);
gp.addAdjaNode(n2, n4, 5);
gp.addAdjaNode(n4, n2, 5);
gp.addAdjaNode(n3, n4, 3);
gp.addAdjaNode(n4, n3, 3);
gp.addAdjaNode(n3, n5, 4);
gp.addAdjaNode(n5, n3, 4);
gp.addAdjaNode(n4, n5, 2);
gp.addAdjaNode(n5, n4, 2);
gp.addAdjaNode(n4, n6, 3);
gp.addAdjaNode(n6, n4, 3);
gp.addAdjaNode(n5, n6, 5);
gp.addAdjaNode(n6, n5, 5);
// 下面嘗試一下非連通圖
// /**
// * 權值: 1
// * A -----------B
// * 權 | *
// * 值 | * 權值: 3
// * 2 | *
// * C-----D
// * 權值: 5
// *
// *
// * */
//
// gp.addAdjaNode(n1, n2, 1);
// gp.addAdjaNode(n2, n1, 1);
//
// gp.addAdjaNode(n1, n3, 2);
// gp.addAdjaNode(n3, n1, 2);
//
// gp.addAdjaNode(n1, n4, 3);
// gp.addAdjaNode(n4, n1, 3);
//
// gp.addAdjaNode(n3, n4, 5);
// gp.addAdjaNode(n4, n3, 5);
gp.prinGraph();
System.out.println("--------------------------------------------------------------------");
System.out.println("此陣列下標代表id,值代表從源點分別到各點的最短距離, A開始的下標是0, B、C、D等依次類推, 並且源點預設設定為id為零0的開始");
gp.findMinPath();
System.out.println(Arrays.toString(gp.minPath));
}
}
/**
* 頂點類
*/
class Node1{
String name;
boolean visited = false; // 訪問狀態。有效 減少原演算法移除V集合中元素所花費的時間
int id = -1; // 設定預設id為-1
ArrayList<Float> distonext = new ArrayList<>(); //這一點 到另外每一個點的距離
public Node1(int id, String name) {
this.id = id;
this.name = name;
}
}