拓扑排序
应用场景:
例如选课,施工过程不可能出现两个课程的先选可都是互相或者施工中的两个项目形成先行必要的条件,那么这种应用场景下的图必须是无环图。
- AOV网(Activity On Vertex Network):用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,称之为AOV网。
什么是拓扑排序?
拓扑排序就是说,在一个有n个顶点的有向图中,V中的顶点序列满足v1-vj的路径中,必有vi在vj之前。则我们称之为拓扑序列。(也就是要保证这个网图无环)
拓扑排序的算法
如何判断一个图是否有环?
无向图
无向图:如果存在回路,则必存在一个子图,是一个环路。环路中的所有顶点的度>=2.
算法1:
第一步:删除所有度<=1的顶点及相关的边,并将另外与这些边相关的其它顶点的度减1.
第二步:将度数为1的顶点排入队列中,并从该队列中取出一个顶点重复步骤一。
如果最后还有未删除顶点,则存在环,否则没有。
n算法分析:
由于有m条边,n个顶点。
- 如果m>=n,则根据图论知识可直接判断存在环路。(证明:如果没有环路,则该图必然是k棵树 k>=1。根据树的性质,边的数目m = n-k。k>=1,所以:m<n)
- 如果m<n 则按照上面的算法每删除一个度为0的顶点操作一次(最多n次),或每删除一个度为1的顶点(同时删一条边)操作一次(最多m次)。这两种操作的总数不会超过m+n。由于m<n,所以算法复杂度为O(n)。
有向图
有向图:
1.统计各个图中各个点的入度数(能够到达这个点的点的数量)。
2.然后找出入度数为0的点(无向图找入度数为1的点)。
3.删除入度数为0的点,将其边也删除。
4.重复2,直到所有点入度都为0,则为无环图,如果找不到入度为0的点,则为有环图。
用一个课程表的例题来说明拓扑排序:
题目描述:
现在你总共有 n 门课需要选,记为 0 到 n-1。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?
思路:
这道题就是典型的拓扑排序的应用例题。
每个课程都看成一个结点。
课程的先修课程看成是一个有向的,整个连起来就是一个有向图。
只要判断一个有向图中是否有环就行。
如果有环则不可能完成课程的学习,如果无环则可以完成。
C++代码实现:
class Solution {
public:
//拓扑排序
void GetDegreein(vector<int>& degreein,vector<vector<int>>& prerequisites)
{
for(int i=0;i<prerequisites.size();++i)
{
degreein[prerequisites[i][1]]++;
}
}
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
if(numCourses<=0||prerequisites.size()==0||prerequisites[0].size()==0)
return true;
vector<int> degreein(numCourses);
stack<int> st;
GetDegreein(degreein,prerequisites);
for(int i=0;i<degreein.size();++i)
{
if(degreein[i]==0)
st.push(i);
}
int count=0;
while(!st.empty())
{
int top=st.top();
count++;
st.pop();
for(int i=0;i<prerequisites.size();++i)
{
if(top==prerequisites[i][0])
{
int nextnode=prerequisites[i][1];
if(!--degreein[nextnode])
st.push(nextnode);
}
}
}
if(count<numCourses)
return false;
return true;
}
};