拓扑排序(图)

拓扑排序原理(本质是图):

用顶点表示活动的网络 (AOV网络)

DFS的逆序就是拓扑排序(邓俊辉mooc有),由0出度的点依次放入栈中,然后栈的输出就是拓扑排序

计划、施工过程、生产流程、程序流程等都是“工程”。除了很小的工程外,一般都把工程分为若干 个叫做“活动”的子工程。完成了这些活动,这个工程就可以完成了。

例如,计算机专业学生的学习就是一个工程,每一 门课程的学习就是整个工程的一些活动。其中有些 课程要求先修课程,有些则不要求。这样在有的课 程之间有领先关系,有的课程可以并行地学习。

 可以用有向图表示一个工程。在这种有向图 中,用顶点表示活动,用有向边表示<Vi ,Vj>活动Vi必须先于活动Vj进行。这种有向图叫做 顶点表示活动的AOV网络 。 • 在AOV网络中不能出现有向回路, 即有向环。 如果出现了有向环,则意味着某项活动应以自 己作为先决条件。 • 因此,对给定的AOV网络,必须先判断它是否 存在有向环。

进行拓扑排序的步骤:

① 输入AOV网络。令 n 为顶点个数。

② 在AOV网络中选一个入度为0的结点, 并输出之;(关键步)

③ 从图中删去该顶点, 同时删去所有它发出的有向 边;

④ 重复以上 ②、③步, 直到下面的情况之一出现: (1)全部顶点均已输出,拓扑有序序列形成,拓 扑排序完成; (2)图中还有未输出的顶点, 但已没有入度为0的 结点(说明网络中必存在有向环)。 

 

 

在邻接表中增设一个数组count[],记录各顶点入度。

入度为零的顶点即无前驱顶点。

在输入数据前, 顶点表NodeTable[]和入度数组count[]全部初始化。

在输入数据时, 每输入一条边<j, k>, 就需要建立一个边结点, 并将它链入相应边链表中, 统计入度信息:    

Edge * p = new Edge<int> (k);            //建立边结点, dest 域赋为 k    

p->link = NodeTable[j].adj;    

NodeTable[j].adj = p;                  //链入顶点j的边链表的前端    

count[k]++;        //顶点k入度加一  

template <class T, class E>
void TopologicalSort (Graph<T, E>& G) {
int i, j, w, v;
    int top = -1;              //入度为零顶点的栈初始化
    int n = G.NumberOfVertices();    //网络中顶点个数
    int *count = new int[n];    					                            //入度数组兼入度为零顶点栈
    for (i = 0; i < n; i++) count[i] = 0;
    cin >> i >> j;   	       //输入一条边(i, j)
    while (i > -1 && i < n && j > -1 && j < n) {
        G.insertEdge (i, j);  count[j]++;
        cin >> i >> j; 
    }   
	for (i = 0; i < n; i++)      	//检查网络所有顶点
        if (count[i] == 0)        	//入度为零的顶点进栈
            { count[i] = top;  top = i; }
    for (i = 0; i < n; i++)      	//期望输出n个顶点
        if (top == -1) {           	//中途栈空,转出
            cout << "网络中有回路!" << endl;
            return;
        }
        else {                               	//继续拓扑排序
            v = top;  top = count[top];   	//退栈v
            cout << G.getValue(v) << "  " << endl;   //输出
            w = G.GetFirstNeighbor(v);            
		  while (w != -1) {    	 //扫描顶点v的出边表
                 count[w]--; 		 //邻接顶点入度减一
                 if (!count[w])               //入度减至零,进栈
                    { count[w] = top;  top = w; }
                 w = G.GetNextNeighbor (v, w);
		  }     //一个顶点输出后,调整其邻接顶点入度
        }      //输出一个顶点,继续for循环
};

参考博客:

拓扑排序入门(真的很简单)_安得广厦千万间的博客-CSDN博客_拓扑排序

 


更新 

vector实在用于邻接表太棒了,都不用考虑指针

#include<iostream>
#include <vector>
#include <string>
#include <stack>
using namespace std;
const int N = 10010;
int in[N];      //入度辅助数组
vector<int>v[N];       //vector实现有向邻接表
vector<int>p;  //保存拓扑节点输出的排序
int main()
{
	int n, m;
    cout<<"请输入图的顶点个数与边数:";
	cin >> n >> m;
	memset(in, 0, sizeof(in));   //快速把in[]入度数组全部置0
	for (int i = 0; i < n; i++) //把邻接表全部清空数据
                v[i].clear();   //可以类比二级指针申请空间去理解
	cout<<"请依次输入图每一条边的起点与终点:";
	for (int i = 0; i < m; i++)
	{
		int from, to;       //  from------>to   有向边
		cin >> from >> to;
		v[from].push_back(to);      //把有向边的终点存入顶点为from的vector中
		in[y]++;                //此时有向边的终点入度+1
	}
	stack<int>s;            //利用栈保存全部入度为0的顶点
	for (int i = 0; i < n; i++)
	{
		if (!in[i]) //如果入度为0,存入栈中,如果
        s.push(i);
	}

    int node=n;
	while (!s.empty())
	{
		int startnode = s.top();   //startnode为拓扑排序的起点遍历
		p.push_back(startnode);//也可以直接输出,不用存进这个vector容器也行
		node--;                 //每遍历一个节点,节点数目减1
		s.pop();                //把0入度节点弹出栈
		for (int i = 0; i < v[startnode].size(); i++)
		{
			int nextnode = v[startnode][i];  //逐个遍历以startnode节点的下一个节点
			in[nextnode]--;       //nextnode节点入度减1,因为已经遍历一次
			if (!in[nextnode])    //如果在遍历的过程中发现入度节点为0的点放入栈中
			{
				s.push(nextnode);
			}
		}
    }
	if (!node)  //如果node为0,说明图中无环且全部输出(存入了p数组)
	{
		for (const auto &item : p) cout << item << " ";
		cout << endl;
	}
	else 
        cout << "错误!该有向图存在环" << endl;

	return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值