[学习笔记]拓扑排序

北城以北 提交于 2019-11-27 22:33:32
-拓扑排序学习
a.简述拓扑排序:说是排序 其实根本不是根据单个数据本身而建立的排序 而是根据数据与数据之间的联系而排序 
如果遇到一系列的问题:要求A事件要在B事件前完成 B事件要在D事件后完成....诸如此类就要找到ABCD...之间的发生先后次序
拓扑排序就是解决此类问题的
这里介绍到一个专有名词依赖解析 此类问题就叫做依赖解析 
b.拓扑的前提
拓扑排序适用于DAG(有环无向图)中 否则会出现 循环依赖/无依赖 关系
摘自简书dalao的专业证明(反证法)
当且仅当一个有向图为有向无环图(directed acyclic graph,或称DAG)时,才能得到对应于该图的拓扑排序。每一个有向无环图都至少存在一种拓扑排序。该论断可以利用反证法被证明如下:
假设我们有一由v_1v_n这n个结点构成的有向图,且图中v_1,v_2,...,v_n这些结点构成一个环。这即是说对于所有1≤i<n-1,图中存在一条有向边从v_i指向v_i+1。同时还存在一条从v_n指向v_1的边。假设该图存在一个拓扑排序。
那么基于这样一个有向图,显然我们可以得知对于所有1≤i<n-1v_i必须在v_i+1之前被遍历,也就是v_1必须在v_n之前被遍历。同时由于还存在一条从v_n指向v_1的边v_n必须在v_1之前被遍历。这里出现了与我们的假设所冲突的结果。因此我们可以知道,该图存在拓扑排序的假设不成立。也就是说,对于非有向无环图而言,其拓扑排序不存在。
c.拓扑排序实现思路:
根据前文 我们要找最先做的事件设为A 所以对于A来说没有其他事件指向它(因为是DAG 所以一定存在事件A) 也就是该事件的入度为0 当我们完成A事件时 就要删去A指向的事件(因为此时A已经完成 A指向的事件没有与A事件的关系束缚)继续找入度为0的事件....直到整张图被搜完结束
 
d.拓扑排序算法描述:
  • 初始化一个int[] inDegree保存每一个结点的入度。
  • 对于图中的每一个结点的子结点,将其子结点的入度加1。
  • 选取入度为0的结点开始遍历,并将该节点加入输出。
  • 对于遍历过的每个结点,更新其子结点的入度:将子结点的入度减1。
  • 重复步骤3,直到遍历完所有的结点。
  • 如果无法遍历完所有的结点,则意味着当前的图不是有向无环图。不存在拓扑排序。
//这样可以判断一个图是否存在环
 
e.拓扑排序代码实现:
 
 1 //topo sort;
 2 #include <bits/stdc++.h>
 3 using namespace std;
 4 #define N 10000+10
 5 
 6 int in[N];
 7 int n,m;
 8 
 9 queue<int>q;//需要字典序最小:priority_queue<int,vector<int>,greater<int> > q; /整数<int> q;
10 vector<int>edge[N];
11 void Topo()
12 {
13     for (int i=1;i<=n;i++) if (in[i]==0) q.push(i);
14     vector<int> ans;
15     while (!q.empty())
16     {
17         int q1=q.front(); q.pop();
18         ans.push_back(q1);
19         for (int i=0;i<edge[q1].size();i++)
20         {
21             int x=edge[q1][i];
22             in[x]--;
23             if (in[x]==0) q.push(x);    
24         }    
25     }
26     if (ans.size()==n)
27     {
28         for (int i=0;i<ans.size();i++)
29             printf("%d ",ans[i]);
30         printf("\n");
31         return ;
32     }
33     else printf("No Answer!");
34 }  
35 
36 int main()
37 {
38     memset(in,0,sizeof(0));
39     scanf("%d%d",&n,&m);
40     for (int i=1;i<=m;i++)
41     {
42         int x,y;
43         scanf("%d%d",&x,&y);
44         edge[x].push_back(y);
45         in[y]++; 
46     }
47     Topo();
48     return 0;
49 }

//ps:还了解到了有反向拓扑之类的套路题 之后会update

 

//topo sort;
#include <bits/stdc++.h>
using namespace std;
#define N 10000+10

int in[N];
int n,m;

queue<int>q;//需要字典序最小:priority_queue<int,vector<int>,greater<int> > q; /整数<int> q;
vector<int>edge[N];
void Topo()
{
    for (int i=1;i<=n;i++) if (in[i]==0) q.push(i);
    vector<int> ans;
    while (!q.empty())
    {
        int q1=q.front(); q.pop();
        ans.push_back(q1);
        for (int i=0;i<edge[q1].size();i++)
        {
            int x=edge[q1][i];
            in[x]--;
            if (in[x]==0) q.push(x);    
        }    
    }
    if (ans.size()==n)
    {
        for (int i=0;i<ans.size();i++)
            printf("%d ",ans[i]);
        printf("\n");
        return ;
    }
    else printf("No Answer!");
}  

int main()
{
    memset(in,0,sizeof(0));
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        edge[x].push_back(y);
        in[y]++;
    }
    Topo();
    return 0;
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!