# 最短路径(算法第四版)(C#)

#### 最短路径的性质

1. 路径是有向的
2. 不需要所有边都可达
3. 负权重会使问题变得更复杂
4. 最短路径不是唯一的

#### 有向加权图的数据结构

public class DirectEdge
{
private int v;  //起点
private int w;  //终点
private double weight;

public DirectEdge(int form, int to, double weight)
{
this.v = form;
this.w = to;
this.weight = weight;
}
public int Form => v;
public int To => w;
public double Weight => weight;
}


//加权有向图
public class EdgeWeightedDigraph
{
private int v;
private int e;

public EdgeWeightedDigraph(string path)
{
string line;
e = 0;
for (int i = 0; i < v; i++) { _adj[i] = new List<DirectEdge>(); }
while ((line = sr.ReadLine()) != null)
{
string[] str = line.Split(' ');
int a = Convert.ToInt32(str[0]);
int b = Convert.ToInt32(str[1]);
float w = (float)Convert.ToDouble(str[2]);
}
}

public EdgeWeightedDigraph(int v)
{
for (int i = 0; i < v; i++)
{
}
this.v = v;
e = 0;
}

{
e++;
}

public int V => v;
public int E => e;

{
}
//将加权有向图转换为有向图，后面使用拓扑排序会用到
public Digraph ToDigraph()
{
Digraph g= new Digraph(v);
for (int i = 0; i < _adj.Length; i++)
{
{
}
}
return g;
}
}


#### 最短路径的API

public class SP
SP(EdgeWeightedDigraph G,int s)  //构造函数
double DistTo(int v)             //s到v的距离，不存在时为无穷大
bool HasPathTo(int v)            //是否可达
DirectEdge[] PathTo(int v)


#### Dijkatra算法

/// <summary>
/// 求得正权重加权有向图的最短路径
/// </summary>
public class DijkstraSP
{
private DirectEdge[] edgeTo;
private double[] distTo;
private IndexMinPQ<double> pq;

public DijkstraSP(int v, EdgeWeightedDigraph g)
{
edgeTo = new DirectEdge[g.V];
distTo = new double[g.V];
pq = new IndexMinPQ<double>(g.V);
for (int i = 0; i < g.V; i++)
distTo[i] = Double.MaxValue;
distTo[v] = 0;
pq.Insert(v, 0);
while (!pq.IsEmpty())
{
Relax(g, pq.DeleteMin());
}
}

/// <summary>
/// 松弛边，选择权更小的路径
/// </summary>
private void Relax(EdgeWeightedDigraph g,int v)
{
{
int w = edge.To;
if (distTo[v] + edge.Weight < distTo[w])
{
distTo[w] = distTo[v] + edge.Weight;
edgeTo[w] = edge;
if (pq.Contains(w)) pq.Change(w, distTo[w]);
else pq.Insert(w, distTo[w]);
}
}
}

public double DistTo(int v)
{
return distTo[v];
}

public bool HasPathTo(int v)
{
return distTo[v] < Double.MaxValue;
}

public DirectEdge[] PathTo(int v)
{
List<DirectEdge> edges = new List<DirectEdge>();
int w = v;
while (edgeTo[w] != null)
{
w = edgeTo[w].Form;
}
return edges.ToArray();
}
}


#### 处理无环加权有向图的最短路径

1. 能在线性时间处理单点最短路径问题
2. 能处理负权重的边
3. 能解决相关问题，例如找到最长路径

public class AcyclicSP
{
private DirectEdge[] edgeTo;
private double[] distTo;

public AcyclicSP(EdgeWeightedDigraph g)
{
edgeTo = new DirectEdge[g.V];
distTo = new double[g.V];
for (int i = 0; i < g.V; i++) distTo[i] = Double.MaxValue;

Topological topological = new Topological(g.ToDigraph());
distTo[topological.Order[0]] = 0; //起点dist为0
foreach (int v in topological.Order)
{
Relax(g, v);
}
}
/// <summary>
/// 松弛顶点
/// </summary>
private void Relax(EdgeWeightedDigraph g, int v)
{
{
int w = edge.To;
if (distTo[v] + edge.Weight < distTo[w])
{
distTo[w] = distTo[v] + edge.Weight;
edgeTo[w] = edge;
}
}
}
//同Dijkatra
public double DistTo(int v){}
public bool HasPathTo(int v){}
public DirectEdge[] PathTo(int v){}
}


#### 一般加权有向图的最短路径

1. 负权重可能会使我们为了经过负权重绕弯，或者在遍历图后，有结点经过一条负权重的边后还能使路径变短
2. 负权重环绕负权重环走可以使路径变得任意小

##### Bellman-Ford算法

 public class BellmanFordSP
{
private Queue<int> queue;
private double[] distTo;
private DirectEdge[] edgeTo;
private bool[] onQueue; //结点是否已经入队，防止重复入队。
private int cost; //Relax调用次数
private int[] cycle; //是否有负权重环

public BellmanFordSP(EdgeWeightedDigraph g, int s)
{
queue = new Queue<int>();
distTo = new double[g.V];
edgeTo = new DirectEdge[g.V];
onQueue = new bool[g.V];
for (int i = 0; i < distTo.Length; i++)
{
distTo[i] = Double.MaxValue;
}
distTo[s] = 0;
queue.Enqueue(s);
onQueue[s] = true;
while (!HasNegativeCycle && queue.Count > 0)
{
int v = queue.Dequeue();
onQueue[v] = false;
Relax(g, v);
}
}

private void Relax(EdgeWeightedDigraph g, int v)
{
{
int w = e.To;
if (distTo[w] > distTo[v] + e.Weight)
{
distTo[w] = distTo[v] + e.Weight;
edgeTo[w] = e;
if (!onQueue[w])
{
queue.Enqueue(w);
onQueue[w] = true;
}
}

if (cost++ % g.V == 0) FindNegativeCycle();
}
}

/// <summary>
/// 找出负权重环
/// </summary>
private void FindNegativeCycle()
{
int v = edgeTo.Length;
EdgeWeightedDigraph spt = new EdgeWeightedDigraph(v);
for (int i = 0; i < v; i++)
if (edgeTo[i] != null)
//书上用的一个定制的类，我没有找到，就用的有向环代替的
DirectCycle directCycle = new DirectCycle(spt.ToDigraph());
cycle = directCycle.Cycle;
}

public bool HasNegativeCycle => cycle != null;

public int[] NegativeCycle => cycle;

public double DistTo(int v)
{
return distTo[v];
}

public bool HasPathTo(int v)
{
return distTo[v] < Double.MaxValue;
}

public DirectEdge[] PathTo(int v)
{
List<DirectEdge> edges = new List<DirectEdge>();
int w = v;
while (edgeTo[w] != null)
{
w = edgeTo[w].Form;
}
return edges.ToArray();
}
}


05-24 47

03-09 6348

07-12 1013

05-23 357

07-01 3250

03-05 906

#### 【优化算法】遗传算法求最短路径

©️2020 CSDN 皮肤主题: 游动-白 设计师: 上身试试

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、C币套餐、付费专栏及课程。