NeetCode刷题第17天(2025.1.27)

086 Course Schedule II 课程安排二

您将获得一个数组 prerequisites ,其中 prerequisites[i] = [a, b] 表示如果您想学习课程 a ,则必须先学习课程 b 。

  • 例如, [0, 1] 对表示要学习课程 0 ,您必须首先学习课程 1 。

您总共需要学习 numCourses 门课程,标记为 0 到 numCourses - 1 。
返回您可以完成所有课程的有效课程顺序。如果有很多有效答案,则返回其中任何一个。如果无法完成所有课程,则返回空数组。

示例1:
Input: numCourses = 3, prerequisites = [[1,0]]
Output: [0,1,2]
说明:我们必须确保课程 0 在课程 1 之前学习。

示例2:
Input: numCourses = 3, prerequisites = [[0,1],[1,2],[2,0]]
Output: []
说明:不可能完成所有课程。

解题1: 循环检测(DFS)

class Solution:
    def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
        prereq = {c:[] for c in range(numCourses)}
        for crs, pre in prerequisites:
            prereq[crs].append(pre)
        # 1个课程有3个可能的状态
        # visited -> crs  has been added to output
        # visiting -> crs not added to output, but added to cycle
        # unvisited -> crs not added to output or cycle
        output = []
        visit, cycle = set(), set()
        def dfs(crs):
            if crs in cycle:
                return False
            if crs in visit:
                return True
            
            cycle.add(crs)
            for pre in prereq[crs]:
                if dfs(pre) == False:
                    return False
            cycle.remove(crs)
            visit.add(crs)
            output.append(crs)
            return True

        for c in range(numCourses):
            if dfs(c) == False:
                return []
        return output

时间复杂度: O ( V + E ) (V+E) (V+E)
空间复杂度: O ( V + E ) O(V+E) O(V+E)
V 是顶点数,E 是边的数量。

087 Graph Valid Tree 图有效树

给定从 0 到 n - 1 标记的 n 节点和无向边列表(每条边是一对节点),编写一个函数来检查这些边是否组成一棵有效的树。

示例1:
Input: n = 5,  edges = [[0, 1], [0, 2], [0, 3], [1, 4]]
Output: true

示例2:
Input: n = 5, edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]]
Output: false

解题1: 循环检测(DFS)
在这里插入图片描述
首先使用深度优先遍历,并把每次遍历到的节点放入visited中,遍历完后如果visited的个数等于总的节点个数,那么说明他们都是联通的。
在这里插入图片描述

此外,我们还要保证不存在循环。所以我们会为每个节点记录一个prev来表示他的父节点,以防止错误判断存在循环。

class Solution:
    def validTree(self, n: int, edges: List[List[int]]) -> bool:
        if not n:
            return True
        
        adj = {i:[] for i in range(n)}
        for n1, n2 in edges:
            adj[n1].append(n2)
            adj[n2].append(n1)
        
        visit = set()
        def dfs(i, prev):
            if i in visit:
                return False

            visit.add(i)
            for j in adj[i]:
                if j == prev:
                    continue
                if not dfs(j, i):
                    return False
            return True
        return dfs(0, -1) and n == len(visit)

时间复杂度: O ( V + E ) (V+E) (V+E)
空间复杂度: O ( V + E ) O(V+E) O(V+E)
V 是顶点数,E 是边的数量。

088 Number of Connected Components in an Undirected Graph 无向图中的连接组件数量

有一个带有n节点的无向​​图。还有一个edges阵列,其中edges[i] = [a, b]表示图表中的节点a和节点b之间存在边缘。
节点编号为0到n - 1 。
返回该图中连接的组件的总数。

在这里插入图片描述

示例1:
Input: n=5, edges=[[0,1], [1,2], [3,4]]
Output: 2

解题1:
在这里插入图片描述
这里我们有两个变量,par和rank。这里par(parent)是用来标记祖先节点的。这里0节点和1节点之间存在边,0位置的rank首先会+1。1和2之间又存在边,实际上2就是0的子孙,因此0位置的rank会再+1。

class Solution:
    def countComponents(self, n: int, edges: List[List[int]]) -> int:
        par = [i for i in range(n)]
        rank = [1] * n

        # 查找每个节点的顶级父节点
        def find(n1):
            res = n1

            while res != par[res]:
                # 如果存在连接,就把它连到最高级的父节点上
                par[res] = par[par[res]]
                res = par[res]
            return res

        def union(n1, n2):
            p1, p2 = find(n1), find(n2)
            if p1 == p2:
                return 0
            if rank[p2] > rank[p1]:
                par[p1] = p2
                rank[p2] += rank[p1]
            else:
                par[p2] = p1
                rank[p1] += rank[p2]
            return 1
        
        res = n
        for n1, n2 in edges:
            res -= union(n1, n2)
        return res

时间复杂度: O ( V + E ) O(V+E) O(V+E)
空间复杂度: O ( V + E ) O(V+E) O(V+E)
V 是顶点数,E 是边的数量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值