1.拓扑排序的理解
拓扑排序是用于有向、无环图里,将优先的节点先输出的一种排序方式。有向是指节点与节点之间有一个优先关系,例如想烧一壶水,那么必须先用水壶去接水,再用水壶去烧水,其中接水就是烧水的优先动作,想烧水就必须先接水。无环是指一系列的动作中必须是有一个入口和一个出口的,举一个有环的例子,先有鸡还是先有蛋,鸡要蛋孵,蛋要鸡下(不从科学角度考虑,例子的设定就是这样),这个例子中就出现了有环的现象,鸡和蛋之前互为优先关系,有环的图是无法进行拓扑排序的。
2.偏序和全序
只要是满足了有向、无环的图就满足偏序关系。那么如果要满足全序就得多加一个条件,有向、无环图中任意两顶点之间还需要有明确的关系。先说一个偏序的例子,小明有买菜和炒菜两件事要做,买菜的顺序是先到菜市场,再买菜,最后回家;炒菜的顺序是先到冰箱拿菜出来,再洗菜,最后炒菜。在这两件事情中不分先后,那么就可能会出现两种情况,先买菜后炒菜或者先炒菜后买菜,这就是偏序。如果是全序的情况下这个例子就会增加一个新的条件,小明的冰箱里没有菜了,他需要先去买菜之后才可以炒菜,这样出现的情况就只有一种情况,这就是全序。
3.算法思路
(1)找到一个没有后继的定点存放到栈中(得到一个倒序的顺序)
(2)从图中删除该定点和它的弧(与其他定点的关系)
(3)出栈(得到正序的拓扑排序结果)
4.代码
/*拓扑排序算法*/
/// <summary>
/// 寻找没有后继点的节点(出度为0)
/// 邻接矩阵的一行全为0
/// </summary>
/// <returns></returns>
private int FindNoSuccessor()
{
bool isEdge;
for (int i = 0; i < numVert; i++) //numVert是图中的顶点数
{
isEdge = false;
for (int j = 0; j < numVert; j++)
{
if (adjmatrix[i, j] != 0)
{
isEdge = true;
break;
}
}
if (!isEdge)
return i;
}
return -1;
}
/// <summary>
/// 从顶点数组中删除点
/// 从临接矩阵中删除点
/// </summary>
/// <param name="vert"></param>
private void DelVertex(int vert)
{
if (vert <= numVert - 1)
{
//从顶点数组中删除点
for (int i = vert; i < numVert; i++)
{
vertices[i] = vertices[i + 1]; //vertices是用于存储图的数组
}
//从临接矩阵中删除点
//删除行
for (int i = vert; i < numVert; i++)
{
MoveRow(i, numVert);
}
//删除列
for (int i = vert; i < numVert; i++)
{
MoveCol(i, numVert - 1);
}
numVert--;
}
}
/// <summary>
/// 移动临接矩阵中的行
/// </summary>
/// <param name="row"></param>
/// <param name="lenth"></param>
private void MoveRow(int row, int lenth)
{
for (int col = row; col < lenth; col++)
{
adjmatrix[row, col] = adjmatrix[row + 1, col]; //adjmatrix是用于存放定点间关系的矩阵(临接矩阵)
}
}
/// <summary>
/// 移动列
/// </summary>
/// <param name="col"></param>
/// <param name="lenth"></param>
private void MoveCol(int col, int lenth)
{
for (int row = col; row < lenth; row++)
{
adjmatrix[row, col] = adjmatrix[row, col + 1];
}
}
/// <summary>
/// 拓扑排序
/// 将出度为0的节点入栈,这样就可以把拓扑排序的顺序从后往前保存起来,
/// 再从栈里取出,顺序就是从前往后的。
/// </summary>
public void TopSort()
{
int origVerts = numVert;
//存放返回节点的栈
System.Collections.Stack result = new Stack();
while (numVert > 0)
{
//找到第一个没有后继节点的节点
int currVertex = FindNoSuccessor();
if (currVertex == -1)
{
Console.WriteLine("图为环路图,不能搞拓扑排序");
return;
}
//如果找到,将其加入返回结果栈
result.Push(vertices[currVertex].Data);
//然后删除此节点
DelVertex(currVertex);
}
/*输出排序后的结果*/
Console.Write("拓扑排序的顺序为:");
while (result.Count > 0)
{
Console.Write(result.Pop() + " ");
}
Console.WriteLine();
}