拓扑排序

基本概念:

一个有向无环图的拓扑序列是将图中的顶点排成一个线性序列,使得对于图中任意一对顶点u和v。若图中存在边<u,v>,则线性序列中

u在v之前出现。

(一)Khan算法:

①: 若图中剩余的点的入度均大于0则该图不存在拓扑序列,否则进行步骤②;

②: 取一个入度为0的顶点u并放置序列末尾;

③: 删除点u及点u伸出的所有边,同时与点u相连的点入度减1;

④: 若图中还存在顶点,再进行步骤①。

代码实现:

#define N 10005
bool vis[N];
int head[N], degree[N];
int Q[N], cnt, num;
struct EdgeNode{
	int to;
	int next;
};
EdgeNode edge[N];

//计算入度与出度 
void add(int u, int v)
{
	degree[v]++;
	edge[cnt].to++;
	edge[cnt].next = head[u];
	head[u] = cnt++;
}

void Topsort(int n)
{
	num = 0;
	for(int i = 1; i <= n; i++){
		if(!degree[i] && vis[i])	//先将没有前驱的结点加入队列 
			Q[num++] = i;
		for(int i = 0; i < num; i++){
			//删除从该结点出发的所有边,更新degree数组
			for(int k = head[Q[i]]; k != -1; k = edge[k].next){
				degree[edge[k].to]--;
				//如果degree数组为0,说明新的没有前驱的结点找到 ,加入队列中
				if(!degree[edge[k].to]) 
					Q[num++] = edge[k].to;
			} 
		}
	}
} 
(二)基于DFS算法:


代码实现:

1.

int timef = 0;
int n ;
int a[1000][1000];// 图的邻接矩阵
int f[1000];  //完成时间
int vis[1000];  //1代表 被发现 2代表 已完成

void DFS(int u)
{
    vis[u] = 1;   //记录发现时刻
    for(int v=1; v<=n; v++){ 		//adj(u)   //O(E)
        if(a[u][v] && vis[v]==0)
        	DFS(v);
    }
    Vis[u] = 2;  //记录完成时刻
    timef++;
    f[u] = timef;
}

void DFS_main()   //O(V+E)
{
    timef = 0;
    for(int i=1; i<=n; i++){            // O(V)
        if(vis[i] == 0)
            DFS(i);
    }
}

void Topological_sort()      //O(V+E)
{
    int tp[1000];            //存放拓扑序列1..V
    DFS_main();
    for(int i=1; i<=n; i++)   //按finish的时间倒序存放在tp序列tp中
    	tp[n-f[i]+1] = i;
    for(int i=1; i<=n; i++)
        cout<<tp[i]<<" ";
    cout<<endl;
}

2.

#include <iostream>  
#include <cstdio>  
#include <cstring>  
using namespace std;  
  
#define maxn 100  //最大顶点个数  
int n, m;         //顶点数,边数  
  
struct arcnode  //边结点  
{  
    int vertex;     //与表头结点相邻的顶点编号  
    arcnode * next; //指向下一相邻接点  
    arcnode() {}  
    arcnode(int v):vertex(v),next(NULL) {}  
};  
  
struct vernode      //顶点结点,为每一条邻接表的表头结点  
{  
    int vex;       //当前定点编号  
    arcnode * firarc;   //与该顶点相连的第一个顶点组成的边  
      
}Ver[maxn];  
  
void Init()  //建立图的邻接表需要先初始化,建立顶点结点  
{  
    for(int i = 1; i <= n; i++)  
    {  
        Ver[i].vex = i;  
        Ver[i].firarc = NULL;  
    }  
}  
  
void Insert(int a, int b)   //插入以a为起点,b为终点,无权的边  
{  
    arcnode * q = new arcnode(b);  
    if(Ver[a].firarc == NULL)  
        Ver[a].firarc = q;  
    else  
    {  
        arcnode * p = Ver[a].firarc;  
        while(p->next != NULL)  
            p = p->next;  
        p->next = q;  
    }  
}  
  
#define INF 9999  
bool visited[maxn];         //标记顶点是否被考察,初始值为false  
int parent[maxn];           //parent[]记录某结点的父亲结点,生成树,初始化为-1  
int d[maxn], time, f[maxn]; //时间time初始化为0,d[]记录第一次被发现时,f[]记录结束检查时  
int topoSort[maxn];  
int cnt;  
void dfs(int s)         //深度优先搜索(邻接表实现),记录时间戳,寻找最短路径  
{  
    //cout << s << " ";  
    visited[s] = true;  
    time++;  
    d[s] = time;  
    arcnode * p = Ver[s].firarc;  
    while(p != NULL)  
    {  
        if(!visited[p->vertex])  
        {  
            parent[p->vertex] = s;  
            dfs(p->vertex);  
        }  
        p = p->next;  
    }  
    time++;  
    f[s] = time;  
    topoSort[cnt++] = s;      //DFS拓扑序列逆序存放在topoSort[ ]中,因为先被保存的一定是搜索树叶子节点  
  
}  
void dfs_travel()       //遍历所有顶点,找出所有深度优先生成树,组成森林  
{  
    for(int i = 1; i <= n; i++)     //初始化  
    {  
        parent[i] = -1;  
        visited[i] = false;  
    }  
    time = 0;  
    for(int i = 1; i <= n; i++)     //遍历  
        if(!visited[i])  
            dfs(i);  
    //cout << endl;  
}  
void topological_Sort()  
{  
    cnt = 0;  
    dfs_travel();  
    for(int i = cnt-1; i >= 0; i--)  
        cout << topoSort[i] << " ";  
    cout << endl;  
}  
int main()  
{  
    int a, b, w;  
    cout << "Enter n and m:";  
    cin >> n >> m;  
    Init();  
    while(m--)  
    {  
        cin >> a >> b;       //输入起点、终点  
        Insert(a, b);        //插入操作  
    }  
    topological_Sort();  
    return 0;  
}

例题:HDU--4857

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <vector>  
#include <queue>  
#define maxn 30005  
using namespace std;  
  
int n,m;  
bool vis[maxn];  
int in[maxn];  
vector<int> edge[maxn];  
priority_queue<int> q;  

void topsort(int n)
{
    for(int i = 1; i <= n; i++){ 
        if(in[i]==0) 
            q.push(i);  
    }  
    int ans = 0;  
    vector<int> res;  
    int u, v;
    while(!q.empty()){  
        ans++;  
        u = q.top();  
        res.push_back(u);  
        q.pop();  
        vis[u]=1;  
        for(int i = 0; i<edge[u].size(); i++){  
            v = edge[u][i];  
            in[v]--;  
            if(in[v] == 0) 
                q.push(v);  
        }  
       }
       for(int i = res.size()-1; i >= 0; i--){  
        if(i == res.size()-1) 
            printf("%d",res[i]); 
        else 
            printf(" %d",res[i]); 
    } 
}
int main()  
{  
    int t;  
    scanf("%d", &t);  
    while(t--)  
    {  
        scanf("%d %d",&n,&m);  
        memset(in, 0, sizeof(in));  
        for(int i = 1; i <= n; i++) 
            edge[i].clear();  
        int u,v;  
        while(m--){ 
            scanf("%d %d",&u,&v);  
            in[u]++;  
            edge[v].push_back(u);  
        }  
        memset(vis, 0, sizeof(vis));  
           topsort(n);
           puts("");
    }  
    return 0;  
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值