UVA 10305 例题6-15

拓扑排序

定义:在一个有向图中对每一个节点进行排序,使其每一个节点都不会指向前面的节点就是拓扑排序。(根据定义很容易得到有环的话是无法进行拓扑排序的。)

由于有些节点之间并无直接联系,所以节点无法比较其大小,但知道其大致放在那个方位。

如:若有4个变量,已知a<b,c<b,d<c则四个变量的大小关系可以为a<d<c<b也可以为d<a<c<b。

拓扑排序的方法可以分为两种:

一、 kahn算法
将节点看成有向联通图

  1. 每一个节点都有一个入度

  2. 每个节点都有上(下)一个节点

    起始时入度为0的节点为起始点,通过它们向下面的节点遍历(放入队列),遍历到下面的节点则删去其之间的连线(也就是下面一个节点的入度减一),若下面一个节点的入度为0的话则将其放入队列,向下遍历。直到队列中无元素为止。

    遍历所有节点,若还是存在入度不为0的节点,则该连通图存在环,则不能拓扑排序,若全部为0,则按照放入队列的顺序输出节点即可。

优先队列的拓扑排序: 用于有特殊限制的拓扑排序,如:需按字典序输出,或者先输入较小的节点。

//
// Created by fallrain on 2020/8/23.
//

#include<bits/stdc++.h>
using namespace std;
int n,m;
map<int,int>mp;
vector<int>v[105];
queue<int>q;
vector<int >ans;
int main()
{
    while(cin>>n>>m) {
        for(int i=0;i<n;i++)
        {
            v[i].clear();
        }
        while(!q.empty())
            q.pop();
        ans.clear();
        mp.clear();
        if(n==0&&m==0)
            break;
        for (int i = 0; i < m; i++) {
            int a, b;
            cin >> a >> b;
            v[a].push_back(b);
            mp[b]++;
        }
        for (int i = 1; i <= n; i++) {
            if (mp[i] == 0) {
                ans.push_back(i);
                q.push(i);
            }
        }
        while (!q.empty()) {
            int next = q.front();
            q.pop();
            for (int i = 0; i < v[next].size(); i++) {
                mp[v[next][i]]--;
                if (mp[v[next][i]] == 0) {
                    q.push(v[next][i]);
                    ans.push_back(v[next][i]);
                }
            }
        }
        for (int i = 0; i < ans.size(); i++) {
            cout << ans[i] << " ";
        }
        cout<<endl;
    }
    return 0;
}

二、 dfs排序(栈+dfs)

思维:在拓扑排序中,部分点是没有关系的,所以可以任意决定它们的位置,在这里插入图片描述
只需要判断某一条链上的顺序就行其他的链并没有特定的关系。故而可以一条链一条链的判断,遍历到最后一个元素之后递归并将元素放入栈中,并记录该元素已经访问过,不需要再放入栈中,再将栈中的元素输出,即可得到结果。(判断环,则是找某一条链有无某个元素的下一个元素指向前方的元素,若指向则是环,不能拓扑排序。)(但是该题没有必要,所以我就没写了😁(关键是懒~))

//
// Created by fallrain on 2020/8/24.
//

#include <bits/stdc++.h>
using namespace std;
int n,m;
stack<int>s;
vector<int>v[105];
map<int,int>mp;
set<int>ss;
void dfs(int num)
{
    for(int i=0;i<v[num].size();i++)
    {
        dfs(v[num][i]);
    }
    if(!ss.count(num))
    {
        s.push(num);
        ss.insert(num);
    }
    return ;
}
int main()
{
    while (cin >> n >> m)
    {
        while(!s.empty())
        {
            s.pop();
        }
        ss.clear();
        mp.clear();
        for(int i=1;i<=n;i++)
            v[i].clear();
        if(n==0&&m==0)
            break;
        for(int i=0;i<m;i++)
        {
            int a,b;
            cin>>a>>b;
            mp[b]++;
            v[a].push_back(b);
        }
        for(int i=1;i<=n;i++)
        {
            if(mp[i]==0)
            {
                dfs(i);
            }
        }
        while(!s.empty())
        {
            cout<<s.top()<<" ";
            s.pop();
        }
        cout<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值