基本概念
- 生成树:设G为连通网,具有G的所有顶点(假设为n个)且只有n-1条边的连通子网。
- 树的权:生成树T的各边的权值的和。
- 最小生成树:权值最小的生成树。
Prim算法
算法描述:
public class SpanTreeNode//最小生成树的结点
{
public string ParentName { get; }//双亲节点的名称
public string SelfName { get; }//自己的名称
public double Weight { get; set; }//父结点到双亲结点的路径的权值
public SpanTreeNode(string selfname, string parentname, double weight)
{
if (string.IsNullOrEmpty(selfname) || string.IsNullOrEmpty(parentname))
{
throw new ArgumentNullException();
//此处最小生成树中的结点的名字不能为空,但是图的顶点名字可以为空。
}
ParentName = parentname;
SelfName = selfname;
Weight = weight;
}
}
//贪心算法,不一定是最优
public SpanTreeNode[] MiniSpanTree(string vName)//从Vname这个结点开始构造最小生成树
{
int i = GetIndex(vName);
if (i == -1)
{
return null;
}
SpanTreeNode[] SpanTree = new SpanTreeNode[VertexCount];//最小生成树
SpanTree[0] = new SpanTreeNode(vName, "Null", 0.0);
//记录U中的结点(并非特指一个,重点在于权值最小)到V-U中各个结点最小权值对应的那个结点的index
//vertexIndex中的下标对应的是U中的结点,即记录父结点
int[] vertexIndex = new int[VertexCount];
//记录U中结点(泛指)到V-U中各结点的最小权值。下标代表V中结点的index
double[] LowCost = new double[VertexCount];
for (int j = 0; j < VertexCount; j++)
{
vertexIndex[j] = i;
LowCost[j] = double.MaxValue;
}
EdgeNode p1 = _vertexList[i].FirstNode;
while (p1 != null)
{
LowCost[p1.Index] = p1.Weight;
p1 = p1.Next;
}
vertexIndex[i] = -1;//vertexIndex=-1代表这个点已经是U中的点
//找出与U中结点相连的权值最小的V-U中的结点以及最小权值
for (int count = 1; count < VertexCount; count++)
{
double min = double.MaxValue;//最小权值
int v = i;//最小权值对应的V-U中的结点
for (int k = 0; k < VertexCount; k++)
{
if (vertexIndex[k] != -1 && min > LowCost[k])
{
min = LowCost[k];
v = k;
}
}
SpanTree[count] = new SpanTreeNode(_vertexList[v].VertexName,
_vertexList[vertexIndex[v]].VertexName, min);
vertexIndex[v] = -1;//v这个结点已经是U中的结点
EdgeNode temp = _vertexList[v].FirstNode;
while (temp != null)
{
if (vertexIndex[temp.Index] != -1 && LowCost[temp.Index] > temp.Weight)
{
LowCost[temp.Index] = temp.Weight;
vertexIndex[temp.Index] = v;
}
temp = temp.Next;
}
}
return SpanTree;
}
Kruskar算法(全局最优)
难点:不同的连通分量如何体现
通过记录父结点的数组找出始祖。
//Kruskar算法(全局最优)
//首先构造边的集合(按照权重由小到大的顺序)
public class Edge
{
public int Begin { get; }
public int End { get; }
public double Weight { get; }
public Edge(int begin, int end, double weight)
{
Begin = begin;
End = end;
Weight = weight;
}
}
public Edge[] GetEdges()
{
//边不可重复,(u,v)=(v,u)
for (int i = 0; i < VertexCount; i++)
{
_vertexList[i].Visited = false;
}
//List实际上是动态数组
List<Edge> result = new List<Edge>();//不知道一共有多少条边
for (int i = 0; i < VertexCount; i++)
{
EdgeNode temp = _vertexList[i].FirstNode;
while (temp != null)
{
if (_vertexList[temp.Index].Visited == false)
{
result.Add(new Edge(i, temp.Index, temp.Weight));
}
temp = temp.Next;
}
_vertexList[i].Visited = true;
}
return result.OrderBy(a => a.Weight).ToArray();//a代表result里面的元素
}
private int FindAncestor(int[] parent, int f)//找出标号为f的结点的祖先(始祖)
{
while (parent[f] > -1)
{
f = parent[f];
}
return f;//无论如何不会返回-1
}
public SpanTreeNode[] MiniSpanTree()
{
int[] parent = new int[VertexCount];
for (int i = 0; i < VertexCount; i++)
{
parent[i] = -1;
}
Edge[] edge = GetEdges();
SpanTreeNode[] tree = new SpanTreeNode[VertexCount];
int count = 1;
for (int i = 0; i < edge.Length; i++)
{
int begin = FindAncestor(parent, edge[i].Begin);
int end = FindAncestor(parent, edge[i].End);
if (begin != end)//这条边的起点和终点不在同一个连通分量上
{
parent[end] = begin;//关键点
tree[count] = new SpanTreeNode(_vertexList[edge[i].End].VertexName,
_vertexList[edge[i].Begin].VertexName, edge[i].Weight);
count++;
}
}
for (int i = 0; i < VertexCount; i++)
{
if (parent[i] == -1)
{
tree[0] = new SpanTreeNode(_vertexList[i].VertexName, "Null", 0.0);
}
}
return tree;
}