拓扑排序——平行课程(leetcode 1136)

2023-10-27 15:10

本文主要是介绍拓扑排序——平行课程(leetcode 1136),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目描述

已知有 N 门课程,它们以 1 到 N 进行编号。

给你一份课程关系表 relations[i] = [X, Y],用以表示课程 X 和课程 Y 之间的先修关系:课程 X 必须在课程 Y 之前修完。

假设在一个学期里,你可以学习任何数量的课程,但前提是你已经学习了将要学习的这些课程的所有先修课程。

请你返回学完全部课程所需的最少学期数。

如果没有办法做到学完全部这些课程的话,就返回 -1。

示例 1:

输入:N = 3, relations = [[1,3],[2,3]]
输出:2
解释:
在第一个学期学习课程 1 和 2,在第二个学期学习课程 3。

示例 2:

输入:N = 3, relations = [[1,2],[2,3],[3,1]]
输出:-1
解释:
没有课程可以学习,因为它们相互依赖。

提示:

    1 <= N <= 5000
    1 <= relations.length <= 5000
    relations[i][0] != relations[i][1]
    输入中没有重复的关系

算法分析

根据题目的描述和示例,我们首先应该想到本题很适合图的算法。那么我们就以图的形式将题目中的信息建模。

    令 G(V,E)G(V, E)G(V,E) 为一个有向,无权图。
    每门课程为图中的一个结点。
    根据课程之间的先修关系对边进行建模。问题中给出的 relations[i] = [X, Y] 的二元组代表课程 X 是课程 Y 的先修课程。这可以用一条有向边表示。
    图 GGG 有可能是循环图,如果是循环图,则不可能完成所以课程。

下面我们根据一个示例来建图,示例为 relations = [[0,2],[1,3],[2,1],[2,3],[2,4],[3,5],[6,5]。

对于上述图,可能的一种上课方法是:0 6 2 1 4 3 5,还有一种可能的方法为:0 2 1 3 4 5 6。当然还有其他可能的方法。这是两种不同的排序方法,第一种是根据拓扑排序,第二种则是深度优先遍历的方法。本题使用第一种方法更加合适。
方法一:拓扑排序

思路

这题其实是典型的有优先级限制的调度问题,我们可以使用拓扑排序来解决这样的问题。

我们知道只有当一门课程没有前置课程的时候,才能上这门课。我们可以把这门课程需要上的前置课程的数量表示为入度。比如上图中 0 的入度为 0,没有前置课程需要上,而 3 的入度为 2,有 1 和 2 需要上。所以我们可以每一次遍历把所有入度为 0 的课程上完,并且把他们的后置课程的入度减 1,重复这个过程直到没有课程可以上。

算法

    初始化所有课程的入度 preClassCount和直接后置课程 nextClasses。
    初始化 term 记录一共遍历了几次,一次代表一个学期。
    找出所有入度为 0 的课程,记录为已学习 learn。
    将所有这学期已学习的课程的直接后置课程的入度减 1。
    重复第三步直到所有课程已经学完,或者这个学期不能学习任何课程。

广度优先搜索

class Solution {
public:int minimumSemesters(int n, vector<vector<int>>& relations) {vector<vector<int>> edges(n);vector<int> ins(n);queue<int> que;for(auto& v : relations) {edges[v[0]-1].push_back(v[1]-1);++ins[v[1]-1];}for(int i =0; i < n; ++i) {if(ins[i] == 0) {que.push(i);}}int ans = 0;int count = 0;while(!que.empty()) {int size = que.size();++ans;while(size) {--size;int v = que.front();que.pop();count++;for(auto& neighbor : edges[v]) {--ins[neighbor];if(ins[neighbor] == 0) {que.push(neighbor);}}}}return count == n? ans:-1;}
};

算法复杂度分析

时间复杂度:O(E+V)。这里 E表示邻边的条数,V表示结点的个数。初始化入度为 0 的集合需要遍历整张图,具体做法是检查每个结点和每条边,因此复杂度为 O(E+V),然后对该集合进行操作,又需要遍历整张图中的每个结点和每条边,复杂度也为 O(E+V)。

空间复杂度:O(E+V)。这里 E 表示邻边的条数,V 表示结点的个数。入度数组长度为节点个数 V,二维数组记录后置节点的长度为 E。

这篇关于拓扑排序——平行课程(leetcode 1136)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/286476

相关文章

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时

leetcode-24Swap Nodes in Pairs

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode swapPairs(L

leetcode-23Merge k Sorted Lists

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode mergeKLists

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

《数据结构(C语言版)第二版》第八章-排序(8.3-交换排序、8.4-选择排序)

8.3 交换排序 8.3.1 冒泡排序 【算法特点】 (1) 稳定排序。 (2) 可用于链式存储结构。 (3) 移动记录次数较多,算法平均时间性能比直接插入排序差。当初始记录无序,n较大时, 此算法不宜采用。 #include <stdio.h>#include <stdlib.h>#define MAXSIZE 26typedef int KeyType;typedef char In

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return