【板子】拓扑排序

有向无环图

如果一个有向图的任一个点都无法通过一些有向边回到自身,则称这个图为 有向无环图 DAG

拓扑排序

如果存在边(u,v),则拓扑排序中u一定在v的前面。

具体实现

  • 定义一个队列,把所有入度为 0 的结点加入到队列。
  • 取队首结点,输出。删去所有从他出发的边,并令这些边的另一端的结点入度 - 1。如果某个顶点的入度减为 0,则将其入队。
  • 反复执行,直到队列为空。
  • 如果输出的结点数恰好为 n,则拓扑排序成功,图 G 为有向无环图 DAG,反之,图 G 中有环。

【注】邻接表实现比较好。

cpp 复制代码
// 邻接表实现
vector<int> g[maxn];
int n,m,inD[maxn];

bool topologicalSort()
{
	int num=0;
    queue<int> q;
    vector<int> inD(n, 0);
    vector<vector<int> > g(n + 1);

    for(auto x:edges) 	// 邻接表存储边(不是邻接矩阵!)
    {
        g[x[1]].emplace_back(x[0]);
        inD[x[0]]++;
    }
    
    for(int i = 0; i < n; i++)
        if(inD[i] == 0) 	//入度为 0 的顶点入队列
            q.push(i);
            
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        num++; 	// 每取出一个入度为 0 的点,num++
        for(int i = 0; i < g[u].size(); i++) 	// 遍历邻接点,修改邻接点的入度
        {
            int v = g[u][i];
            inD[v]--; 	//入度--
            if(inD[v] == 0) 	// 出现了新的入度为 0 的顶点,入队
                q.push(v); 
        }
        // g[u].clear(); 	//清空顶点u出发的所有边,可忽略
    } 
    if(num == n) return true;
    return false;
}


//邻接矩阵实现
    bool topologicalSort(){
        vector<vector<int> > g(n + 1, vector<int>(n + 1, 0));
        vector<int> in(n + 1, 0);
        vector<int> vis(n + 1, 0); 	// 额外建立一个数组记录顶点的访问情况

        for(auto x:edges) 	// 邻接矩阵存储边
        {
            g[x[1]][x[0]] = 1;
            in[x[0]]++;
        }
        
        int cnt = 0;

        while(cnt < n) 	// 当获取 n 个入度为 0 的顶点时,结束循环
        {
            int u = -1;
            for(int i = 0; i < n; i++)
            {
                if(in[i] == 0 && vis[i] == 0) 	// 入度为 0 且未被访问过
                {
                    u = i;
                    vis[u] = 1;
                    break;
                }
            }
            if(u == -1) return false;

            for(int i = 0; i < numCourses; i++)
            {
                if(g[u][i] == 1) 	//存在边,入度--
                    in[i]--;
            }
            cnt++;
        }
        return true;
    }
};

【注】 如果要求有多个入度为 0 的顶点时选择序号最小的,把queue改成priority_queue,或者set也可以。

【注】 邻接表 + 队列 实现时,出现的入度为 0 的顶点一定是之前未被访问过的 (因为被遍历到的邻接点 v 至少存在边 (u, v) ,即,入度至少为1),所以不需要 vis 数组;而邻接矩阵实现时,每一次都会遍历所有顶点,所以需要记录那些入度为 0 的顶点已经被计算过了,所以需要 vis 数组,进而防止重复计算。


相关推荐
unicrom_深圳市由你创科技2 分钟前
上位机开发常用的语言 / 框架有哪些?
c++·python·c#
|_⊙1 小时前
C++ 智能指针
开发语言·c++
memcpy01 小时前
LeetCode 2452. 距离字典两次编辑以内的单词【暴力;字典树】中等
算法·leetcode·职场和发展
Jasmine_llq1 小时前
《B4356 [GESP202506 二级] 数三角形》
开发语言·c++·双重循环枚举算法·顺序输入输出算法·去重枚举算法·整除判断算法·计数统计算法
山栀shanzhi1 小时前
在做直播时,I帧的间隔(GOP)一般是多少?
网络·c++·面试·ffmpeg
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【排序贪心】:魔法
c++·算法·贪心·csp·信奥赛·排序贪心·魔法
晓觉儿2 小时前
【GPLT】2026年第十一届团队程序设计天梯赛赛后题解(已写2h,存档中)
数据结构·c++·算法·深度优先·图论
We་ct2 小时前
LeetCode 322. 零钱兑换:动态规划入门实战
前端·算法·leetcode·typescript·动态规划
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 394. 字符串解码 | C++ 单栈回压法
c++·算法·leetcode
穿条秋裤到处跑3 小时前
每日一道leetcode(2026.04.22):距离字典两次编辑以内的单词
算法·leetcode