特别鸣谢:来自夸夸群的 醉笑陪公看落花@知乎,王不懂不懂@知乎,
感谢醉笑陪公看落花@知乎 倾囊相授,感谢小伙伴们督促学习,一起进步
相关文章:
- leetcode 743. 网络延迟时间- 有向图 - 图的一些小知识点
- 链表找环 leetcode 141 , 142 及进阶:空间复杂度O(1)
- leetcode 847. 访问所有节点的最短路径 -比特位表示访问情况-双端队列和字典初始值设置-BFSvsDFS
802. 找到最终的安全状态
在有向图中,以某个节点为起始节点,从该点出发,每一步沿着图中的一条有向边行走。如果到达的节点是终点(即它没有连出的有向边),则停止。
对于一个起始节点,如果从该节点出发,无论每一步选择沿哪条有向边行走,最后必然在有限步内到达终点,则将该起始节点称作是 安全 的。
返回一个由图中所有安全的起始节点组成的数组作为答案。答案数组中的元素应当按 升序 排列。
该有向图有 n 个节点,按 0 到 n - 1 编号,其中 n 是 graph 的节点数。图以下述形式给出:graph[i] 是编号 j 节点的一个列表,满足 (i, j) 是图的一条有向边。

输入:graph = [[1,2],[2,3],[5],[0],[5],[],[]]
输出:[2,4,5,6]
解释:示意图如上。
题目分析
找环,能走到环上的节点排除之后,剩下的节点就是安全路径
解法1 -用二进制数表示节点访问情况- 超时
- cover : 记录路径上使用过的结点
- circle : 记录经过环的结点
class Solution:
def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
return solve1(graph)
def solve1(graph):
N = len(graph)
circle = 0
for i in range(N):
circle |= DFS(graph,0,i,circle)
return showCover(2**N-1 ^ circle)
#
def DFS(graph,cover,i,circle):
cover2 = cover | 1 << i
if (circle | 1<<i )== circle or cover == cover2:return circle|cover
for j in graph[i]:
circle |= DFS(graph,cover2,j,circle)
return circle
'''
打印cover情况
'''
def showCover(cover):
bicover = bin(cover)[2:]
bicovers = []
for j in range(len(bicover)-1,-1,-1):
if bicover[j] == '1': bicovers.append(len(bicover)-j-1)
return bicovers
解法2 - 三色标记- 通过
- 0 未访问
- 1 访问但是发现节点不安全
- 2 访问发现结点安全
- 3 标记为访问
3和1,2 可以合并
主要思想: 某个结点是否安全,要看它的所有后裔是否都是安全的
class Solution:
def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
return solve5(graph)
def solve5(graph):
# 出度
cd = [len(x) for x in graph]
g = graphReverse(graph)
q = [x for x in range(len(cd)) if cd[x]==0]
while q:
i = q.pop()
for j in g[i]:
cd[j]-=1
if cd[j] == 0:
q.append(j)
ans = [i for i in range(len(cd)) if cd[i]==0]
return ans
'''
狼牙数组转置
'''
def graphReverse(graph):
# a->b b->a
# 出度换成入度
p = [[] for i in range(len(graph))]
# 遍历了一遍节点和边 n+m
for i in range(len(graph)):
for j in graph[i]:
p[j].append(i)
return p
二进制位表示节点访问情况
打印访问过的结点
def showCover(cover):
bicover = bin(cover)[2:]
bicovers = []
for j in range(len(bicover)-1,-1,-1):
if bicover[j] == '1': bicovers.append(len(bicover)-j-1)
return bicovers
数组转置
狼牙数组(同一维度,长度不固定), 普通二维列表 转置
方法1
def graphReverse(graph):
p = [[] for i in range(len(graph))]
# 遍历了一遍节点和边 n+m
for i in range(len(graph)):
for j in graph[i]:
p[j].append(i)
return p
方法2
def listReverse(graph):
return [list(y) for y in zip(*graph)]
方法3
def listReverse(graph):
# return [x for x in map(lambda x:list(x),zip(*graph))]
# return list(map(list,zip(*graph)))
return list(map(lambda x:list(x),zip(*graph)))
星号使用
graph = [[1,2],[2,3],[5],[0],[5],[],[]]
zip(*graph) == zip( [1,2],[2,3],[5],[0],[5],[],[] )
unzip 也有同样的用法
* 表示接收一个参数
** 表示接受键值对


本文探讨了在有向图中识别安全起始节点的两种方法:一是利用二进制位表示节点访问状态,二是采用三色标记策略。通过DFS和回溯,展示了如何判断节点安全性并找出所有安全起点。
864

被折叠的 条评论
为什么被折叠?



