1. 程式人生 > >Dijkstra演算法-(迪傑斯特拉)演算法的迭代實現與優先佇列實現 圖解演算法過程

Dijkstra演算法-(迪傑斯特拉)演算法的迭代實現與優先佇列實現 圖解演算法過程

Dijkstra演算法-(迪傑斯特拉)演算法之迭代實現 Dijkstra演算法-(迪傑斯特拉)演算法之優先佇列實現

該演算法的核心原理,很簡單,如下圖所示:


先說說Dijkstra演算法-(迪傑斯特拉)演算法之迭代實現,如下圖為詳細步驟,


程式碼如下,兩種實現方法都有,用優先佇列實現的演算法更優秀。

package com.collonn.algorithm;

import java.util.PriorityQueue;

/**
 * 帶權有向圖的單源最短路徑<br/>
 * Dijkstra演算法-(迪傑斯特拉)演算法<br/>
 */
public class GrfDijkstra {
	// 為了矩陣的輸出更直觀好看一些,本例中約定,路徑權值取值範圍為:[1,10],權值為999表示無連通
	// 並假設所有權值的和小於999
	private final int MAX = 999;
	// 頂點總數
	private int total;
	// 頂點資訊
	private String[] nodes;
	// 頂點矩陣
	private int[][] matirx;
	// 源點到各頂點的距離
	private int[] dis;
	// 頂點是否已標記
	private int[] mark;

	public GrfDijkstra(int total, String[] nodes) {
		this.total = total;
		this.nodes = nodes;
		this.matirx = new int[total][total];
		this.dis = new int[total];
		this.mark = new int[total];
	}

	private void printMatrix() {
		System.out.println("--------- weighted directed matrix ---------");
		System.out.println("---0---1---2---3---4---5---6---7---8---");
		System.out.println("---A---B---C---D---E---F---G---H---I---");
		for (int i = 0; i < this.total; i++) {
			System.out.print("-" + this.nodes[i] + "|");
			for (int j = 0; j < this.total; j++) {
				System.out.print(String.format("%03d", this.matirx[i][j]) + "-");
			}
			System.out.print("\n");
		}
		System.out.println("--------- weighted directed matrix ---------");
	}

	/**
	 * Dijkstra演算法-(迪傑斯特拉)演算法之迭代實現
	 * 
	 * @param s
	 *            源點(起點)
	 */
	public void dijkstra(int s) {
		// 初始化
		for (int i = 0; i < this.total; i++) {
			// 初始化都未標記
			this.mark[i] = 0;
			// 給源點的直接鄰接點加上初始權值
			this.dis[i] = this.matirx[s][i];
		}

		// 將源點s加入已標記
		this.mark[s] = 1;
		// 頂點到自身的距離為0
		this.dis[s] = 0;
		// 臨時最短距離
		int min = -1;
		// 臨時最短距離的頂點
		int k = -1;

		this.printDis(0, "屌", "初始化");

		// 除去源點s到自身的距離為0外,還要不斷的進行距離修正(源點s到其它頂點(共total-1個)的最短距離)
		for (int i = 1; i < this.total; i++) {
			// 從當前最新的,源點到其它各頂點的距離資訊陣列dis[]中,找到一個沒有標記過的,
			// 並且距離(從源點到該某頂點)最短的頂點
			min = MAX;
			for (int j = 0; j < this.total; j++) {
				if (this.mark[j] == 0 && this.dis[j] < min) {
					min = this.dis[j];
					k = j;
				}
			}

			// 標記該頂點
			this.mark[k] = 1;

			/**
			 * 距離校正<br/>
			 */
			for (int j = 0; j < this.total; j++) {
				if (this.mark[j] == 0 && (this.matirx[k][j] + this.dis[k]) < this.dis[j]) {
					this.dis[j] = this.matirx[k][j] + this.dis[k];
				}
			}

			this.printDis(i, this.nodes[k], "進行中");
		}
	}

	/**
	 * Dijkstra演算法-(迪傑斯特拉)演算法之優先佇列實現
	 */
	public void dijkstraPQ() {
		// Item是PriorityQueue中元素,實現了Comparable介面,優先佇列用此介面進行排序
		class Item implements Comparable<Item> {
			// 節點在陣列的下標
			public int idx;
			// 到改節點的臨時最小權值和
			public int weight;

			public Item(int idx, int weight) {
				this.idx = idx;
				this.weight = weight;
			}

			@Override
			public int compareTo(Item item) {
				if (this.weight == item.weight) {
					return 0;
				} else if (this.weight < item.weight) {
					return -1;
				} else {
					return 1;
				}
			}
		}

		// 值較小的元素總是在優先佇列的頭部,值較大的元素總是在優先佇列的尾部
		PriorityQueue<Item> pq = new PriorityQueue<Item>();

		// 將源點(即起點)加入到優先佇列
		pq.offer(new Item(0, 0));

		Item itm = null;
		while (!pq.isEmpty()) {
			itm = pq.poll();

			// 圖中某節點下標
			int idx = itm.idx;
			// 到某節點的臨時最小權值和
			int weight = itm.weight;

			// 如果該元素還未標記,則更新最小權值各
			if (this.mark[idx] == 0) {
				this.dis[idx] = weight;
			}

			// 找出該元素(假設A)的所有未標記的鄰接點(假設B)
			// 則,源點到B的距離可表示為:(源點到A的距離) + (A到B的距離)
			// 將源點到B的距離加入到優先佇列中
			for (int i = 0; i < this.total; i++) {
				if (this.mark[i] == 0) {
					pq.offer(new Item(i, this.dis[idx] + this.matirx[idx][i]));
				}
			}
		}
	}

	private void printDis(int i, String node, String pre) {
		System.out.print("\n" + pre + "," + node + "," + i + "--->");
		for (int t = 0; t < this.dis.length; t++) {
			System.out.print(t + ",");
		}
		System.out.print("\n" + pre + i + "--->");
		for (int t : this.dis) {
			System.out.print(t + ",");
		}
		System.out.print("\n");
	}

	// 初始化圖資料
	// 0---1---2---3---4---5---6---7---8---
	// A---B---C---D---E---F---G---H---I---
	private void initGrf() {
		// 初始化矩陣為最大值(各節點都不連通)
		for (int i = 0; i < this.total; i++) {
			for (int j = 0; j < this.total; j++) {
				if (i == j) {
					this.matirx[i][j] = 0;
				} else {
					this.matirx[i][j] = MAX;
				}
			}
		}

		// 手動設定有向路徑
		// A->B, A->E, A->D
		this.matirx[0][1] = 2;
		this.matirx[0][4] = 3;
		this.matirx[0][3] = 1;
		// B->C
		this.matirx[1][2] = 2;
		// C->F
		this.matirx[2][5] = 1;
		// D->E, D->G
		this.matirx[3][4] = 5;
		this.matirx[3][6] = 2;
		// E->F, E->H
		this.matirx[4][5] = 6;
		this.matirx[4][7] = 1;
		// F->I
		this.matirx[5][8] = 3;
		// G->H
		this.matirx[6][7] = 4;
		// H->F, H->I
		this.matirx[7][5] = 1;
		this.matirx[7][8] = 2;
	}

	public static void main(String[] args) {
		String[] nodes = new String[] { "A", "B", "C", "D", "E", "F", "G", "H", "I" };
		GrfDijkstra grf = new GrfDijkstra(nodes.length, nodes);
		grf.initGrf();
		grf.printMatrix();

		System.out.println();
		System.out.println("------ Dijkstra演算法-(迪傑斯特拉)演算法之迭代開始 ------");
		grf.dijkstra(0);
		grf.printDis(0, "屌", "最終值");
		System.out.print("\n");
		System.out.println("------ Dijkstra演算法-(迪傑斯特拉)演算法之迭代結束 ------");

		System.out.println();
		System.out.println("------ Dijkstra演算法-(迪傑斯特拉)演算法之優先佇列開始 ------");
		grf.dijkstraPQ();
		grf.printDis(0, "屌", "最終值");
		System.out.print("\n");
		System.out.println("------ Dijkstra演算法-(迪傑斯特拉)演算法之優先佇列結束 ------");
	}
}

原創博文,轉載請註明出處。

右鍵,檢視圖片,看大圖。