算法练习:临接表求有向图拓扑排序

目录

核心代码

临接表、临接表的添加

数据结构

临接表函数

拓扑排序的思路

数据结构

算法思路

例题:有向图拓扑排序

描述

输入 

输出 

输入样例 1 

输出样例 1

题解 


核心代码

临接表、临接表的添加

数据结构

存储临接表各点数据的数组 e [M]

存储临接表各节点关系的数组 ne [M]

存储临接表起点的数组 h[N]

临接表函数

思路:

把同一起点的有向边存储在一起,边的终点组成链表,后来居上,边的起点指向链表的首结点

int idx = 0 ;                //指针 0 
memset(h , -1 , sizeof h)    //把 h 数组赋值为 -1
void add(int a , int b)      //输入:有向边 a->b 的俩结点:a ,b
{
    e[idx] = b;              //记录 数组e 第idx个 结点数据内容为 b
    ne[idx] = h[a];          //这个语句求的是 数组e 第idx个 节点的下一个结点的位置
                                //如果之前有记录以 a 为起点的边
                                    //那这些边的终点值存储在 数组e 中
                                    //这些节点的 ne值 会组成一个链表,后来居上
                                    //h[a] 记录就是这些边终点节点所构成的链表的首位置
                                    //相当于 b 这个节点下一个节点就是过去的链表
                                //如果没有记录过以 a 为起点的边
                                    //此时 h[a] 就是初始值 -1
                                    //这个节点下个点是末尾
                                    //所以这个节点的 ne 值赋值为 -1
    h[a] = idx++;            //记录 a 为起点的有向边,构成的链表的首结点是第 idx 个
}

拓扑排序的思路

数据结构

一位数组队列存储入度为零的节点 queue [N]  

存储入度的数组 in_degree [N]

算法思路

遍历各个节点,找到入度为零的节点,存储入一位数组队列(用hh、tt作为头尾指针)

while(hh <= tt)
    {
        int temp = queue[hh++];
        //遍历这个点各个边的代码
    }

遍历队列中节点的各个边,每遍历一条边把终点入度 -1 ,减完入度为 0 则入队

    for(int i = h[temp] ; i != -1 ; i = ne[i])
            {
                int j = e[i];
                in_degree[j]--;
                if(!in_degree[j])
                {
                    queue[++tt] = j;
                }
            }

如果 tt == n - 1 ,说明各个点都遍历了,图无环,否则说明还有点没遍历,图有环

例题:有向图拓扑排序

描述

给定一个n个点m条边的有向图,点的编号是1到n,图中可能存在重边和自环。

请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出-1。

若一个由图中所有点构成的序列A满足:对于图中的每条边(x, y),x在A中都出现在y之前,则称A是该图的一个拓扑序列。

数据范围:1≤n,m≤10^5

输入 

第一行包含两个整数n和m

接下来m行,每行包含两个整数x和y,表示存在一条从点x到点y的有向边(x, y)。

输出 

共一行,如果存在拓扑序列,则输出拓扑序列。

否则输出-1。

输入样例 1 

3 3
1 2
2 3
1 3

输出样例 1

1 2 3

题解 

*/
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=10;
const int M=2*N;
int e[M] , ne[M] , h[N] , idx = 0 ;  //临接表
int n , m ;                          //结点的个数、输入行数
int in_degree[N] ;                   //入度数组
int queue[N] ;                       //队列

void add(int a , int b)              //临接表添加结点的函数
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
bool topsort()                       //拓扑排序的函数
{
    int hh = 0,tt = -1;
    for(int i = 1 ; i <= n ; i++)
    {
        if(!in_degree[i])  //如果入度为0
        {
            //tt=0,q[++tt]=i;  //之前都是直接q[0]=...,此处要初始化入队的数量众多,最好把tt改成-1
            queue[++tt] = i;
        }
    }
    while(hh <= tt)
    {
        int temp = queue[hh++];
        for(int i = h[temp] ; i != -1 ; i = ne[i])
        {
            int j = e[i];
            in_degree[j]--;
            if(!in_degree[j])
            {
                queue[++tt] = j;
            }
        }
    }
    return tt == n-1;
    
}
int main()
{
    cin>>n>>m;
    memset(h , -1 , sizeof h);
    while(m--)
    {
        int x,y;
        cin>>x>>y;
        add(x , y);
        in_degree[y]++;
    }
    if(topsort())
    {
        for(int i = 0 ; i < n ; i++)
        {
            printf("%d " , queue[i]);
            //cout<<endl;
        }
        cout<<endl;
    }
    else puts("-1");
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值