223 lines
7.0 KiB
C#
223 lines
7.0 KiB
C#
namespace Tnb.Common.Utils
|
||
{
|
||
/// <summary>
|
||
/// 迪杰斯特拉(最短路径算法)
|
||
/// </summary>
|
||
public class Dijkstra
|
||
{
|
||
public static int MAX = int.MaxValue;
|
||
|
||
public int mEdgNum; // 边的数量
|
||
public VNode[] mVexs; // 顶点数组
|
||
private EData[] edges; //边的数组
|
||
/*
|
||
* 创建图
|
||
*
|
||
* 参数说明:
|
||
* vexs -- 顶点数组
|
||
* edges -- 边
|
||
*/
|
||
public Dijkstra(string[] vexs, EData[] edges)
|
||
{
|
||
this.edges = edges;
|
||
// 初始化"顶点数"和"边数"
|
||
int vlen = vexs.Length;
|
||
int elen = edges.Length;
|
||
|
||
// 初始化"顶点"
|
||
mVexs = new VNode[vlen];
|
||
for (int i = 0; i < mVexs.Length; i++)
|
||
{
|
||
mVexs[i] = new VNode();
|
||
mVexs[i].data = vexs[i];
|
||
mVexs[i].firstEdge = null;
|
||
}
|
||
|
||
// 初始化"边"
|
||
mEdgNum = elen;
|
||
for (int i = 0; i < elen; i++)
|
||
{
|
||
// 读取边的起始顶点和结束顶点
|
||
string c1 = edges[i].start;
|
||
string c2 = edges[i].end;
|
||
int weight = edges[i].weight;
|
||
|
||
// 读取边的起始顶点和结束顶点
|
||
int p1 = GetPosition(c1);
|
||
int p2 = GetPosition(c2);
|
||
// 初始化node1
|
||
ENode node1 = new ENode();
|
||
node1.ivex = p2;
|
||
node1.weight = weight;
|
||
// 将node1链接到"p1所在链表的末尾"
|
||
if (p1 != -1)
|
||
{
|
||
if (p1 != -1 && mVexs[p1].firstEdge == null)
|
||
mVexs[p1].firstEdge = node1;
|
||
else
|
||
LinkLast(mVexs[p1].firstEdge, node1);
|
||
}
|
||
|
||
//// 初始化node2
|
||
//ENode node2 = new ENode();
|
||
//node2.ivex = p1;
|
||
//node2.weight = weight;
|
||
//// 将node2链接到"p2所在链表的末尾"
|
||
//if (mVexs[p2].firstEdge == null)
|
||
// mVexs[p2].firstEdge = node2;
|
||
//else
|
||
// LinkLast(mVexs[p2].firstEdge, node2);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 将node节点链接到list的最后
|
||
*/
|
||
private void LinkLast(ENode list, ENode node)
|
||
{
|
||
ENode p = list;
|
||
|
||
while (p.nextEdge != null)
|
||
p = p.nextEdge;
|
||
p.nextEdge = node;
|
||
}
|
||
|
||
/*
|
||
* 返回ch位置
|
||
*/
|
||
private int GetPosition(string ch)
|
||
{
|
||
for (int i = 0; i < mVexs.Length; i++)
|
||
if (mVexs[i].data == ch)
|
||
return i;
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* 获取边<start, end>的权值;若start和end不是连通的,则返回无穷大。
|
||
*/
|
||
private int GetWeight(int start, int end)
|
||
{
|
||
if (start == end)
|
||
return 0;
|
||
|
||
ENode node = mVexs[start].firstEdge;
|
||
while (node != null)
|
||
{
|
||
if (end == node.ivex)
|
||
return node.weight;
|
||
node = node.nextEdge;
|
||
}
|
||
|
||
return MAX;
|
||
}
|
||
|
||
/*
|
||
* Dijkstra最短路径。
|
||
* 即,统计图中"起点D"到其它各个顶点的最短路径。
|
||
*
|
||
* 参数说明:
|
||
* vs -- 起始顶点(start vertex)。
|
||
* prev -- 前驱顶点数组。即,prev[i]的值是"起点D"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。
|
||
* dist -- 长度数组。即,dist[i]是"起点D"到"顶点i"的最短路径的长度。
|
||
*/
|
||
public void CalcDijkstra(int vs, int[] prev, int[] dist)
|
||
{
|
||
//List<T> vertexs = new(); //最短路径串联的点位列表
|
||
// flag[i]=true表示"起点D"到"顶点i"的最短路径已成功获取。
|
||
bool[] flag = new bool[mVexs.Length];
|
||
|
||
// 初始化
|
||
for (int i = 0; i < mVexs.Length; i++)
|
||
{
|
||
flag[i] = false; // 顶点i的最短路径还没获取到。
|
||
prev[i] = 0; // 顶点i的前驱顶点为0。
|
||
dist[i] = GetWeight(vs, i); // 顶点i的最短路径为"起点D"到"顶点i"的权。
|
||
}
|
||
|
||
// 对"起点D"自身进行初始化
|
||
flag[vs] = true;
|
||
dist[vs] = 0;
|
||
HashSet<int> set = new();
|
||
// 遍历mVexs.Length-1次;每次找出一个顶点的最短路径。
|
||
int k = 0;
|
||
for (int i = 1; i < mVexs.Length; i++)
|
||
{
|
||
// 寻找当前最小的路径
|
||
// 即,在未获取最短路径的顶点中,找到离起点D最近的顶点(k)。
|
||
int min = MAX;
|
||
for (int j = 0; j < mVexs.Length; j++)
|
||
{
|
||
if (flag[j] == false && dist[j] < min)
|
||
{
|
||
|
||
min = dist[j];
|
||
k = j;
|
||
set.Add(j);
|
||
}
|
||
|
||
}
|
||
// 标记"顶点k"为已经获取到最短路径
|
||
flag[k] = true;
|
||
// 更新当前最短路径和前驱顶点
|
||
// 即,更新"未获取最短路径的顶点的最短路径和前驱顶点"。
|
||
for (int j = 0; j < mVexs.Length; j++)
|
||
{
|
||
int tmp = GetWeight(k, j);
|
||
tmp = (tmp == MAX ? MAX : (min + tmp)); // 防止溢出
|
||
if (flag[j] == false && (tmp < dist[j]))
|
||
{
|
||
dist[j] = tmp;
|
||
prev[j] = k;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 邻接表中表对应的链表的顶点
|
||
/// </summary>
|
||
public class ENode
|
||
{
|
||
/// <summary>
|
||
/// 该边所指向的顶点的位置
|
||
/// </summary>
|
||
public int ivex; // 该边所指向的顶点的位置
|
||
/// <summary>
|
||
/// 该边的权
|
||
/// </summary>
|
||
public int weight; // 该边的权
|
||
/// <summary>
|
||
/// 指向下一条弧的指针
|
||
/// </summary>
|
||
public ENode nextEdge; // 指向下一条弧的指针
|
||
}
|
||
|
||
/// <summary>
|
||
/// 邻接表中表的顶点
|
||
/// </summary>
|
||
public class VNode
|
||
{
|
||
public string data; // 顶点信息
|
||
public ENode firstEdge; // 指向第一条依附该顶点的弧
|
||
}
|
||
|
||
/// <summary>
|
||
/// 边的结构体
|
||
/// </summary>
|
||
public class EData
|
||
{
|
||
public string start; // 边的起点
|
||
public string end; // 边的终点
|
||
public int weight; // 边的权重
|
||
|
||
public EData(string start, string end, int weight)
|
||
{
|
||
this.start = start;
|
||
this.end = end;
|
||
this.weight = weight;
|
||
}
|
||
}
|
||
}
|