或許在生活中,經常會碰到針對某一個問題,在眾多的限制條件下,如何去尋找一個最優解?可能大家想到了很多諸如“線性規划”,“動態規划”
這些經典策略,當然有的問題我們可以用貪心來尋求整體最優解,在圖論中一個典型的貪心法求最優解的例子就莫過於“最短路徑”的問題。
一:概序
從下圖中我要尋找V0到V3的最短路徑,你會發現通往他們的兩點路徑有很多:V0->V4->V3,V0->V1->V3,當然你會認為前者是你要找的最短
路徑,那如果說圖的頂點非常多,你還會這么輕易的找到嗎?下面我們就要將剛才我們那點貪心的思維系統的整理下。

二:構建
如果大家已經了解Prim算法,那么Dijkstra算法只是在它的上面延伸了下,其實也是很簡單的。
1.邊節點
這里有點不一樣的地方就是我在邊上面定義一個vertexs來記錄貪心搜索到某一個節點時曾經走過的節點,比如從V0貪心搜索到V3時,我們V3
的vertexs可能存放着V0,V4,V3這些曾今走過的節點,或許最后這三個節點就是我們要尋找的最短路徑。
1 #region 邊的信息
2 /// <summary>
3 /// 邊的信息
4 /// </summary>
5 public class Edge
6 {
7 //開始邊
8 public int startEdge;
9
10 //結束邊
11 public int endEdge;
12
13 //權重
14 public int weight;
15
16 //是否使用
17 public bool isUse;
18
19 //累計頂點
20 public HashSet<int> vertexs = new HashSet<int>();
21 }
22 #endregion
2.Dijkstra算法

首先我們分析下Dijkstra算法的步驟:
有集合M={V0,V1,V2,V3,V4}這樣5個元素,我們用
TempVertex表示該頂點是否使用。
Weight表示該Path的權重(默認都為MaxValue)。
Path表示該頂點的總權重。
①. 從集合M中挑選頂點V0為起始點。給V0的所有鄰接點賦值,要賦值的前提是要賦值的weight要小於原始的weight,並且排除已經訪問過
的頂點,然后挑選當前最小的weight作為下一次貪心搜索的起點,就這樣V0V1為挑選為最短路徑,如圖2。
②. 我們繼續從V1這個頂點開始給鄰接點以同樣的方式賦值,最后我們發現V0V4為最短路徑。也就是圖3。
。。。
③. 最后所有頂點的最短路徑就這樣求出來了 。
1 #region Dijkstra算法
2 /// <summary>
3 /// Dijkstra算法
4 /// </summary>
5 public Dictionary<int, Edge> Dijkstra()
6 {
7 //收集頂點的相鄰邊
8 Dictionary<int, Edge> dic_edges = new Dictionary<int, Edge>();
9
10 //weight=MaxValue:標識沒有邊
11 for (int i = 0; i < graph.vertexsNum; i++)
12 {
13 //起始邊
14 var startEdge = i;
15
16 dic_edges.Add(startEdge, new Edge() { weight = int.MaxValue });
17 }
18
19 //取第一個頂點
20 var start = 0;
21
22 for (int i = 0; i < graph.vertexsNum; i++)
23 {
24 //標記該頂點已經使用過
25 dic_edges[start].isUse = true;
26
27 for (int j = 1; j < graph.vertexsNum; j++)
28 {
29 var end = j;
30
31 //取到相鄰邊的權重
32 var weight = graph.edges[start, end];
33
34 //賦較小的權重
35 if (weight < dic_edges[end].weight)
36 {
37 //與上一個頂點的權值累加
38 var totalweight = dic_edges[start].weight == int.MaxValue ? weight : dic_edges[start].weight + weight;
39
40 if (totalweight < dic_edges[end].weight)
41 {
42 //將該頂點的相鄰邊加入到集合中
43 dic_edges[end] = new Edge()
44 {
45 startEdge = start,
46 endEdge = end,
47 weight = totalweight
48 };
49
50 //將上一個邊的節點的vertex累加
51 dic_edges[end].vertexs = new HashSet<int>(dic_edges[start].vertexs);
52
53 dic_edges[end].vertexs.Add(start);
54 dic_edges[end].vertexs.Add(end);
55 }
56 }
57 }
58
59 var min = int.MaxValue;
60
61 //下一個進行比較的頂點
62 int minkey = 0;
63
64 //取start鄰接邊中的最小值
65 foreach (var key in dic_edges.Keys)
66 {
67 //取當前 最小的 key(使用過的除外)
68 if (min > dic_edges[key].weight && !dic_edges[key].isUse)
69 {
70 min = dic_edges[key].weight;
71 minkey = key;
72 }
73 }
74
75 //從鄰接邊的頂點再開始找
76 start = minkey;
77 }
78
79 return dic_edges;
80 }
81 #endregion
總的代碼:復雜度很爛O(N2)。。。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.IO;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
public class Program
{
public static void Main()
{
Dictionary<int, string> dic = new Dictionary<int, string>();
MatrixGraph graph = new MatrixGraph();
graph.Build();
var result = graph.Dijkstra();
Console.WriteLine("各節點的最短路徑為:");
foreach (var key in result.Keys)
{
Console.WriteLine("{0}", string.Join("->", result[key].vertexs));
}
Console.Read();
}
}
#region 定義矩陣節點
/// <summary>
/// 定義矩陣節點
/// </summary>
public class MatrixGraph
{
Graph graph = new Graph();
public class Graph
{
/// <summary>
/// 頂點信息
/// </summary>
public int[] vertexs;
/// <summary>
/// 邊的條數
/// </summary>
public int[,] edges;
/// <summary>
/// 頂點個數
/// </summary>
public int vertexsNum;
/// <summary>
/// 邊的個數
/// </summary>
public int edgesNum;
}
#region 矩陣的構建
/// <summary>
/// 矩陣的構建
/// </summary>
public void Build()
{
//頂點數
graph.vertexsNum = 5;
//邊數
graph.edgesNum = 6;
graph.vertexs = new int[graph.vertexsNum];
graph.edges = new int[graph.vertexsNum, graph.vertexsNum];
//構建二維數組
for (int i = 0; i < graph.vertexsNum; i++)
{
//頂點
graph.vertexs[i] = i;
for (int j = 0; j < graph.vertexsNum; j++)
{
graph.edges[i, j] = int.MaxValue;
}
}
//定義 6 條邊
graph.edges[0, 1] = graph.edges[1, 0] = 2;
graph.edges[0, 2] = graph.edges[2, 0] = 5;
graph.edges[0, 4] = graph.edges[4, 0] = 3;
graph.edges[1, 3] = graph.edges[3, 1] = 4;
graph.edges[2, 4] = graph.edges[4, 2] = 5;
graph.edges[3, 4] = graph.edges[4, 3] = 2;
}
#endregion
#region 邊的信息
/// <summary>
/// 邊的信息
/// </summary>
public class Edge
{
//開始邊
public int startEdge;
//結束邊
public int endEdge;
//權重
public int weight;
//是否使用
public bool isUse;
//累計頂點
public HashSet<int> vertexs = new HashSet<int>();
}
#endregion
#region Dijkstra算法
/// <summary>
/// Dijkstra算法
/// </summary>
public Dictionary<int, Edge> Dijkstra()
{
//收集頂點的相鄰邊
Dictionary<int, Edge> dic_edges = new Dictionary<int, Edge>();
//weight=MaxValue:標識沒有邊
for (int i = 0; i < graph.vertexsNum; i++)
{
//起始邊
var startEdge = i;
dic_edges.Add(startEdge, new Edge() { weight = int.MaxValue });
}
//取第一個頂點
var start = 0;
for (int i = 0; i < graph.vertexsNum; i++)
{
//標記該頂點已經使用過
dic_edges[start].isUse = true;
for (int j = 1; j < graph.vertexsNum; j++)
{
var end = j;
//取到相鄰邊的權重
var weight = graph.edges[start, end];
//賦較小的權重
if (weight < dic_edges[end].weight)
{
//與上一個頂點的權值累加
var totalweight = dic_edges[start].weight == int.MaxValue ? weight : dic_edges[start].weight + weight;
if (totalweight < dic_edges[end].weight)
{
//將該頂點的相鄰邊加入到集合中
dic_edges[end] = new Edge()
{
startEdge = start,
endEdge = end,
weight = totalweight
};
//將上一個邊的節點的vertex累加
dic_edges[end].vertexs = new HashSet<int>(dic_edges[start].vertexs);
dic_edges[end].vertexs.Add(start);
dic_edges[end].vertexs.Add(end);
}
}
}
var min = int.MaxValue;
//下一個進行比較的頂點
int minkey = 0;
//取start鄰接邊中的最小值
foreach (var key in dic_edges.Keys)
{
//取當前 最小的 key(使用過的除外)
if (min > dic_edges[key].weight && !dic_edges[key].isUse)
{
min = dic_edges[key].weight;
minkey = key;
}
}
//從鄰接邊的頂點再開始找
start = minkey;
}
return dic_edges;
}
#endregion
}
#endregion
}

