(转自 http://www.cnblogs.com/ZJUKasuosuo/archive/2012/07/12/2587889.html)
1. 拓扑排序主要有两种算法:方法1:《算法导论》上给出的DFS+时间戳;方法2:求顶点入度+贪心算法。
2. 两种算法的代码分别如下:
View Code
static int f[100]={0};
static int ftime=0;
//递归深度优先搜索
template<typename vertexNametype, typename weight>
void ALGraph<vertexNametype, weight>::DFS()
{
int n=getVertexNumber();
bool *visited=new bool[n];
for (int i=0;i<n;i++)
{
visited[i]=false;
}
for (int i=0;i<n;i++)
{
if (!visited[i])
{
DFS(i,visited);
cout<<endl;
}
}
delete[] visited;
}
template<typename vertexNametype, typename weight>
void ALGraph<vertexNametype, weight>::DFS(int vertexNumber,bool visited[])
{
cout<<getData(vertexNumber)<<" ";
visited[vertexNumber]=true;
Edge<weight> *p=m_vertexArray.at(vertexNumber).pAdjEdges;
while(p!=NULL)
{
if (!visited[p->nDestVertex])
{
DFS(p->nDestVertex,visited);
}
p=p->pNextEdge;
}
//每一个节点访问结束之后,都记录其访问时间。
//在每一个dfs的后面加上这个。
f[vertexNumber]=ftime;
ftime++;
}
//拓扑排序算法一的思想是:用DFS遍历整个图,记录各个节点
//的完成时间,然后按各个节点的完成时间逆序排列,就得到了
//拓扑排序序列
template<typename vertexNametype, typename weight>
void ALGraph<vertexNametype,weight>::Topological_sort1()
{
DFS();
int vectexNo=getVertexNumber();
for (int i=0;i<vectexNo;i++)
{
cout<<i<<" : "<<f[i]<<" ";
}
cout<<endl;
int *tp=new int[vectexNo];
//按访问时间逆序排列
for (int i=0;i<vectexNo;i++)
{
tp[vectexNo-f[i]-1]=i; //位置为0的是值最大的那个,1的是次大的那个。然后逐渐递推。tp等于的i的值是结点的位置。用总数减去时间再-1就是他的位置了。(之所以-1,因为从0开始的。)
//实际上就是将值对调,例如,4,3,2,1,0, 那么对应的位置就是0,1,2,3,4的位置。
}
for (int i=0;i<vectexNo;i++)
{
cout<<getData(tp[i])<<" ";
//得到那个结点的值。
}
cout<<endl;
delete[] tp;
}
####################################################
//求一个图中所有节点的入度
template<typename vertexNametype, typename weight>
void ALGraph<vertexNametype,weight>::FindIndegree(INOUT int* &Ind)
{
assert(Ind);
int vertextNo=getVertexNumber();
for (int i=0;i<vertextNo;i++)
{
Edge<weight> *pE=m_vertexArray.at(i).pAdjEdges;// 找到这个结点的第一个弧
while(pE)
{
Ind[pE->nDestVertex]++; //给那个连接的顶点+1,因为跟他连接的那个顶点有了一个入度。
pE=pE->pNextEdge;
}
}
}
//拓扑排序2:方法一
//首先找到入度为0的节点,访问这个节点,并将这个节点
//的所有出边的邻接顶点的入读减1,访问这个顶点;下一步
//继续寻找入度为0的顶点,直到所有的节点均访问玩位置或者
//图中存在环
//时间复杂度为:O(v2)
//template<typename vertexNametype, typename weight>
//void ALGraph<vertexNametype,weight>::Topological_sort2()
//{
// int vectexNo=getVertexNumber();
// int *InDegree=new int[vectexNo];
// for (int i=0;i<vectexNo;i++)
// {
// InDegree[i]=0;
// }
// FindIndegree(InDegree);
// for (int i=0;i<vectexNo;i++)
// {
// cout<<getData(i)<<":"<<InDegree[i]<<" ";
// }
// cout<<endl;
// bool *visited=new bool[vectexNo];
// vector<vertexNametype> top;
// for (int i=0;i<vectexNo;i++)
// {
// visited[i]=false;
// }
// int count=0;
// while(count++<vectexNo)
// {
// int IndexZero=0;
// for (;IndexZero<vectexNo;IndexZero++)
// {
// if (InDegree[IndexZero]==0 && !visited[IndexZero])
// {
// 当没有环的时候,总会有这么一个满足条件的:总会有一个顶点的入度为0。当找到这个的时候,就跳出,没有的话就一直循环直到 indexzero 达到了最大值。
// break;
}
// }
// if (IndexZero>=vectexNo)
// {
// break;
//这个用来判断终结上面那个循环的,究竟是找到了满足入度为0的顶点,还是没有找到,循环结束了。如果是循环结束了,那么说明有环,后面的没有必要
//继续了,直接跳出就行了。
// }
// visited[IndexZero]=true;
// top.push_back(getData(IndexZero));
// Edge<weight>* pE=m_vertexArray.at(IndexZero).pAdjEdges;
// while(pE)
// {
// InDegree[pE->nDestVertex]--;
// pE=pE->pNextEdge;
// }
// }
// if (count<vectexNo)
// {
//如果没有环,count会一直++,直到=总数的时候才终止循环的,但是有的话就<
// cout<<"Graph Has a cycle!!!"<<endl;
// return ;
// }
// for (int i=0;i<vectexNo;i++)
// {
// cout<<top[i]<<" ";
// }
// cout<<endl;
//}
//在方案一中,每次寻找节点入度为0的节点,一个for
//循环时间复杂度为O(v),改用queue每次将入读为0的节点
//存入队列,省去了下一步寻找入读为0的顶点的过程
template<typename vertexNametype, typename weight>
void ALGraph<vertexNametype,weight>::Topological_sort2()
{
int vectexNo=getVertexNumber();
int *InDegree=new int[vectexNo];
for (int i=0;i<vectexNo;i++)
{
InDegree[i]=0;
}
FindIndegree(InDegree);
queue<int> qu;
int IndexZero=0;
//首先将途中所有入读为0的顶点存入队列
for (;IndexZero<vectexNo;IndexZero++)
{
if (InDegree[IndexZero]==0)
{
qu.push(IndexZero);
}
}
vector<vertexNametype> top;
while(!qu.empty())
{
IndexZero=qu.front();
qu.pop();
top.push_back(getData(IndexZero));
Edge<weight>* pE=m_vertexArray.at(IndexZero).pAdjEdges;
while(pE)
{
InDegree[pE->nDestVertex]--;
if (InDegree[pE->nDestVertex]==0)
{
qu.push(pE->nDestVertex);
}
pE=pE->pNextEdge;
}
}
if (top.size()<vectexNo)
{
cout<<"Graph Has a cycle!!!"<<endl;
return ;
}
for (int i=0;i<vectexNo;i++)
{
cout<<top[i]<<" ";
}
cout<<endl;
}
3. 有人说利用DFS做拓扑排序算法有错,但是本人目前没发现这个算法有什么错误。 4. 拓扑排序可以做很多事情:如判断图中有没有环(利用方法2);排课程表(比如学C++之前一定要先学习C语言)。
5. 方法2的两种实现只有细微的差别,实现2更好一点,时间复杂度低,代码简洁。
6. 算法有不当之处,欢迎指正。
7. 算法测试使用图如下:
8. 程序运行结果(图的创建等基本程序参照随笔:图的相关算法):
A B C D E F G分别对应V1 V2 ... V7