貪心算法-單源最短路徑


算法思想:貪心算法

實際問題:單源最短路徑

編程語言:Java


問題描述

  單源最短路徑算法,又稱迪傑斯特拉算法。其目的是尋找從一個頂點到其余各頂點的最短路徑算法,解決的是有權圖中最短路徑問題。

算法構造

相關解釋

  • 觀測域:假設起點為v點,觀測域便為v點的四周,即v的所有鄰接點;
  • 點集 V:圖中所有點的集合;
  • 點集 S:已經找到最短路徑的終點集合;
  • 數組 D:存儲觀測域內能觀測到的最短路徑,算上起點一共 n 個數值。比如 D[k] 對應在觀測域中能觀測到的,到頂點 k 的最短路徑;
  • 鄰接矩陣 a:存儲着有權圖中的邊的信息,是一個二維數組。比如 a[1][2] = 5 表示在有權圖中,點 1 和點 2 之間有邊,且邊的權值為 5。如果兩點之間沒邊,則用負數或則無窮大(∞)表示。

算法步驟

  • 第一步:初始化點集 S,將起點 v 收入 S 中。初始化數組 D:D[k] = a[v][k];
  • 第二步:找尋次短路徑。即查找數組 D,找出觀測域中最短路徑(v, j):D[j] = min(D[k] | k 不屬於 S)。將點 j 加入點集 S 中;
  • 第三步:將 j 的鄰接點並入觀測域,即用 j 的鄰接點更新數組 D;
  • 第四步:不斷重復第二步和第三步,直到節點全部壓入 S 中為止。

注:貪心算法的思想主要就體現在第二步和第三步之中。

Java 代碼

  本代碼求解的是無向有權圖的最短路徑,如果想求有向有權圖的最短路徑,則只需要將無向圖的鄰接矩陣改為有向圖的鄰接矩陣即可。

import java.util.Scanner;

public class SSSP
{
	public static void main(String[] args)
	{
		Scanner input = new Scanner(System.in);
		
		System.out.print("請輸入圖的頂點和邊的個數(格式:頂點個數 邊個數):");
		int n = input.nextInt(); //頂點的個數
		int m = input.nextInt(); //邊的個數
		
		System.out.println();
		
		int[][] a = new int[n + 1][n + 1];
		//初始化鄰接矩陣
		for(int i = 0; i < a.length; i++)
		{
			for(int j = 0; j < a.length; j++)
			{
				a[i][j] = -1; //初始化沒有邊
			}
		}
		
		System.out.println("請輸入圖的路徑長度(格式:起點 終點 長度):");
		//總共m條邊
		for(int i = 0; i < m; i++)
		{
			//起點,范圍1到n
			int s = input.nextInt();
			//終點,范圍1到n
			int e = input.nextInt();
			//長度
			int l = input.nextInt();
			
			if(s >= 1 && s <= n && e >= 1 && e <= n)
			{
				//無向有權圖
				a[s][e] = l;
				a[e][s] = l;
			}
		}
		
		System.out.println();
		
		//距離數組
		int[] dist = new int[n+1];
		//前驅節點數組
		int[] prev = new int[n+1];
		
		int v =1 ;//頂點,從1開始
		dijkstra(v, a, dist, prev);
	}
	
	/**
	 * 單源最短路徑算法(迪傑斯特拉算法)
	 * @param v 頂點
	 * @param a 鄰接矩陣表示圖
	 * @param dist 從頂點v到每個點的距離
	 * @param prev 前驅節點數組
	 */
	public static void dijkstra(int v, int[][] a, int[] dist, int[] prev)
	{
		int n = dist.length;
		/**
		 * 頂點從1開始,到n結束,一共n個結點
		 */
		if(v > 0 && v <= n)
		{
			//頂點是否放入的標志
			boolean[] s = new boolean[n];
			
			//初始化
			for(int i = 1; i < n; i++)
			{
				//初始化為 v 到 i 的距離
				dist[i] = a[v][i];
				//初始化頂點未放入
				s[i] = false;
				//v到i無路,i的前驅節點置空
				if(dist[i] == -1)
				{
					prev[i] = 0;
				}
				else
				{
					prev[i] = v;
				}
			}
			
			//v到v的距離是0
			dist[v] = 0;
			//頂點放入
			s[v] = true;
			
			//共掃描n-2次,v到v自己不用掃
			for(int i = 1; i < n - 1; i++)
			{
				int temp = Integer.MAX_VALUE;
				//u為下一個被放入的節點
				int u = v;
				
				//這個for循環為第二步,觀測域為v的觀測域
				//遍歷所有頂點找到下一個距離最短的點
				for(int j = 1; j < n; j++)
				{
					//j未放入,且v到j有路,且v到當前節點路徑更小
					if(!s[j] && dist[j] != -1 && dist[j] < temp)
					{
						u = j;
						//temp始終為最小的路徑長度
						temp = dist[j];
					}
				}
				
				//將得到的下一節點放入
				s[u] = true;
				
				//這個for循環為第三步,用u更新觀測域
				for(int k = 1; k < n; k++)
				{
					if(!s[k] && a[u][k] != -1)
					{
						int newdist=dist[u] + a[u][k];
						if(newdist < dist[k] || dist[k] == -1)
						{
							dist[k] = newdist;
							prev[k] = u;
						}
					}
				}
			}
		}
		
		for(int i = 2; i < n; i++)
		{
			System.out.println(i + "節點的最短距離是:"
				+ dist[i] + ";前驅點是:" + prev[i]);
		}

	}
}

運行結果

結果示例


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM