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