Leetcode复盘7——图

Leetcode复盘7——图

导读

二分图

1.判断二分图 / 判断是否为二分图(Leetcode785)

难度:中等Medium

idea: 深度优先搜索(DFS)
把它想象成一个互联连通的不是自上而下的二叉树,一层一层下探,每到新的一层,先把父亲节点染了:visited[v]=color,再下探父亲节点的所有孩子节点for(w: graph[v]),再把它染成另外一个颜色.别忘了每到一层先调用terminator: 即判断是否染过了,若染过了,则看已经染的颜色visited[v]和当前节点要然的颜色color是否一致

代码:
Java版

class Solution {
    public boolean isBipartite(int[][] graph) {
        // 定义 visited 数组,初始值为 0 表示未被访问,赋值为 1 或者 -1 表示两种不同的颜色。 
        int[] visited = new int[graph.length];
        // 因为图中可能含有多个连通域,所以我们需要判断是否存在顶点未被访问,若存在则从它开始再进行一轮 dfs 染色。
        for (int i = 0; i < graph.length; i++) {
            if (visited[i] == 0 && !dfs(graph, i, 1, visited)) {
                return false;
            }
        }
        return true;
    }

    private boolean dfs(int[][] graph, int v, int color, int[] visited) {
        // terminator,每到新的一层看是否被染过了,若是,则看已经染过的颜色和当前要染的是否一致,若不一致,当前层返回false,并且会一直往上都返回false
        if (visited[v] != 0) {
            return visited[v] == color;
        }
        
        // process the current logic,对当前顶点进行染色
        visited[v] = color;
        for (int w: graph[v]) {                                 // 对于当前节点v的每个邻接节点w
            if (!dfs(graph, w, -color, visited)) {              // 在process the current logic那一步都染上和当前层相反的颜色,即-color
                return false;
            }
        }
        return true;
    }
}

拓扑排序

1.课程表 / 课程安排的合法性(Leetcode207)

难度:简单Easy

idea: 广度优先遍历,记录入度
入度: 有多少个节点指向它; 出度: 它指向多少个节点
当某一个课程的入度为0时,证明不需要学习其他课程就可以学它,把它加到待学习的队列,同时以它为先修课程的其他课程入度可以减1了.
定义一个字典,其中key为某门课程,value为以它为先修课程的其他课程(这样方便它没了其他的入度都-1),再定义一个数组,记录所有课程的当前入度是多少
e.g
prerequisites = [[1,0],[2,0]], 即课程1和课程2的先修课均为课程0,入度都为1.
edge = {0:(1,2)} indeg = [0,1,1]

代码:
Python版

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        edges = collections.defaultdict(list)                   # key为课程,value为该课程是哪些课程的先修课程,方便后面-1
        indeg = [0] * numCourses                                # 记录所有课程的当前入度

        # 
        for info in prerequisites:
            edges[info[1]].append(info[0])                      # info[0]为当前课程,info[1]是其先修课程
            indeg[info[0]] += 1
        
        # 刚上来先提取入度为0,可以直接学的课程有哪些,收集到待学习队列q里
        q = collections.deque([u for u in range(numCourses) if indeg[u] == 0])
        result = 0

        while q:                                                # 当待学习队列里有课程时,证明还没完
            result += 1                                         # 都可以学,一个一个来
            u = q.popleft()                                     # 先把可以学的第一个课程u弹出来
            for v in edges[u]:                                  # 对于所有以u为先修课程的课程,入度都可以少1啦
                indeg[v] -= 1                                   # 挨个来,每个课程都有入度-1的机会
                if indeg[v] == 0:                               # 诶,v的入度也减为零了,也可以加入待学习队列惹!
                    q.append(v)

        return result == numCourses                             # 最后看可以学的课程数跟总数是否一致

idea: 深度遍历优先,记录出度
方法与广度优先遍历类似,用visited记录每个节点是否被访问过了,0表示还未访问,1表示正在被访问,2表示已经访问过了(此时即没有出度,也即没有指向别的任何节点)

Python版

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        edges = collections.defaultdict(list)
        visited = [0] * numCourses
        result = list()
        valid = True                                        # 无环即有效,有环即无效,刚开始是有效的

        for info in prerequisites:
            edges[info[1]].append(info[0])
        
        def dfs(u: int):
            nonlocal valid
            visited[u] = 1                                  # 每下探一个节点u,先给它标记"正在访问"
            for v in edges[u]:                              # 对于以节点u为先修课程的其他课程v,继续下探
                if visited[v] == 0:                         # v还没被访问过,下探;当v=2,即"已被访问"也没事,证明它已经被加到res里了,只要不是1就行.
                    dfs(v)
                    if not valid:
                        return
                elif visited[v] == 1:                       # 如果当前访问的一条线又回到"正在访问"的节点,则证明有环,无法全部学习
                    valid = False
                    return
            visited[u] = 2                                  # 当某一路探到底时,是不执行上面for v in edges[u]的,此时标记"已被访问过".并加到最终res结果里
            result.append(u)
        
        for i in range(numCourses):
            if valid and not visited[i]:                    # 有效且当前节点还未被访问,则新开一条线
                dfs(i)
        
        return valid
2.课程表 II / 课程安排的顺序(LeetCode210)

难度:简单Easy

idea: 广度优先遍历,记录入度
入度: 有多少个节点指向它; 出度: 它指向多少个节点
当某一个课程的入度为0时,证明不需要学习其他课程就可以学它,把它加到待学习的队列,同时以它为先修课程的其他课程入度可以减1了.
定义一个字典,其中key为某门课程,value为以它为先修课程的其他课程(这样方便它没了其他的入度都-1),再定义一个数组,记录所有课程的当前入度是多少;因为本题要返回学习课程顺序,故要建一个存放课程顺序的列表list,这一点跟"207课程表I"不同了
e.g
prerequisites = [[1,0],[2,0]], 即课程1和课程2的先修课均为课程0,入度都为1.
edge = {0:(1,2)} indeg = [0,1,1]

代码:
Python版

class Solution:
    def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
        edges = collections.defaultdict(list)   # key为课程,value为该课程是哪些课程的先修课程,方便后面-1
        indeg = [0] * numCourses                # 记录所有课程的当前入度
        result = list()                         # 要返回的课程顺序列表

        # 统计每门课程的入度indeg以及它是哪些课程的先修课程
        for info in prerequisites:
            edges[info[1]].append(info[0])      # info[0]为当前课程,info[1]是其先修课程
            indeg[info[0]] += 1
        
        # 刚上来先提取入度为0,可以直接学的课程有哪些,收集到待学习队列q里
        q = collections.deque([u for u in range(numCourses) if indeg[u] == 0])

        while q:                                # 当待学习队列里有课程时,证明还没完
            u = q.popleft()                     # 先把可以学的第一个课程u弹出来        
            result.append(u)                    # 加到最终可以学习的列表里
            for v in edges[u]:                  # 对于所有以u为先修课程的课程,入度都可以少1啦
                indeg[v] -= 1                   # 挨个来,每个课程都有入度-1的机会
                if indeg[v] == 0:               # 诶,v的入度也减为零了,也可以加入待学习队列惹!
                    q.append(v)

        if len(result) != numCourses:           # 如果此时result的课程数不够,则输出空列表,证明失败了
            result = list()
        return result
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值