添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
神勇威武的蟠桃  ·  Android ...·  2 月前    · 
逼格高的土豆  ·  java.lang.IllegalAcces ...·  3 月前    · 
威武的香菇  ·  Trying to debug PHP ...·  1 年前    · 

对于当前节点 cur 的每一个 neighbour 邻居节点:

  • 没被visted过,看 dfs(neighbour) 是否为true,是的话 dfs(cur) 也return true
  • 被visted过,需要check是一个真的cycle还是说只是回头路。不可以走回头路,只能单向行进
  • if visited[neighbour] and neighbour != parent , then cycle found!
  • def isCyclicUndirectedGraph(graph: List[List[int]], n: int) -> bool:
        visited = [False] * n
        def isCyclicUntil(cur, parent):
            visited[cur] = True
            for neighbour in graph[cur]:
                if not visited[neighbour]:
                    if isCyclicUntil(neighbour, cur):
                        return True
                elif parent != neighbour:  # cycle detected
                    return True
            return False
        for i in range(n):
            if not visited[i]:
                if isCyclicUntil(i, -1):
                    return True
        return False
    

    union find

  • 对edge做去重处理,把双向的两个edge变成只剩下一个
  • 遍历所有edge,如果存在一条edge的两端已经connected,就是环
  • def isCyclicUndirectedGraph(graph: List[List[int]], n: int) -> bool:
        uf = UF(n)
        edges = set()
        for i in range(n):
            for nei in graph[i]:
                edge = tuple(sorted([i, nei]))
                edges.add(edge)
        for x, y in edges:
            if uf.find(x) == uf.find(y):  # if x and y are already in the same component
                return True
            uf.union(x, y)
        return False
    class UF:
        def __init__(self, n):
            self.parent = [i for i in range(n)]
        def find(self, x) -> int:
            if self.parent[x] != x:
                self.parent[x] = self.find(self.parent[x])
            return self.parent[x]
        def union(self, x, y) -> None:
            self.parent[self.find(x)] = self.find(y)
    

    261. 以图判树(Medium)

    Solu 1:DFS + parent nod

    照抄dfs + parent node模版

    Code 1:

    class Solution:
        def validTree(self, n: int, edges: List[List[int]]) -> bool:
            def buildGraph():
                graph = {i: [] for i in range(n)}
                for x, y in edges:
                    graph[x].append(y)
                    graph[y].append(x)
                return graph
            def dfs(cur, prev):
                visited[cur] = True
                for nei in graph[cur]:
                    if not visited[nei]:
                        if dfs(nei, cur):
                            return True
                    elif nei != prev:  # cycle detected
                        return True
                return False
            if len(edges) != n - 1:
                return False
            graph = buildGraph()
            visited = [False] * n
            if dfs(0, -1):  # start from 0 (the only root)
                return False
            return all(visited)  # tree只能有一个component
    

    Solu 2:union find

    照抄union-find模版

    Code 2:

    class Solution:
        def validTree(self, n: int, edges: List[List[int]]) -> bool:
            if len(edges) != n - 1:
                return False
            uf = UF(n)
            for x, y in edges:
                if uf.find(x) == uf.find(y):
                    return False
                uf.union(x, y)
            return True
    class UF:
        def __init__(self, n):
            self.parent = [i for i in range(n)]
        def find(self, x) -> int:
            if self.parent[x] != x:
                self.parent[x] = self.find(self.parent[x])
            return self.parent[x]
        def union(self, x, y) -> None:
            self.parent[self.find(x)] = self.find(y)
    

    有向图找环

  • 有环的图是无法做“拓扑排序”的
  • “拓扑排序”通常用来排序具有依赖关系的任务
  • 一个DAG的“拓扑排序”可能有多个
  • class Solution:
        def isCyclicDirectedGraph(self, graph: List[List[int]], n: int) -> bool:
            inDeg = [0] * n
            for i in range(n):
                for j in graph[i]:
                    inDeg[j] += 1
            queue = [i for i in range(n) if inDeg[i] == 0]
            count = 0
            while queue:
                node = queue.pop(0)
                count += 1
                for nei in graph[node]:
                    inDeg[nei] -= 1
                    if inDeg[nei] == 0:
                        queue.append(nei)
            return count == n
    

    210. 课程表 II(Medium)

    Solu:拓扑排序

    照抄拓扑排序模版

    Code:

    class Solution:
        def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
            indeg = [0] * numCourses
            graph = [[] for _ in range(numCourses)]
            for course, prerequisite in prerequisites:
                indeg[course] += 1
                graph[prerequisite].append(course)
            queue = [i for i in range(numCourses) if indeg[i] == 0]
            res = []
            while queue:
                course = queue.pop(0)
                res.append(course)
                for neighbor in graph[course]:
                    indeg[neighbor] -= 1
                    if indeg[neighbor] == 0:
                        queue.append(neighbor)
            return res if len(res) == numCourses else []
    

    DFS + 双array标记

  • visited:如果一个node已经被visited过了(被check过了),就没必要再DFS一次了
  • 即:代表是否已被其他节点启动的DFS访问
  • recStack:当前recursion中,如果再次遇到了已经遇到过的node,那么有环
  • 即:代表是否已被当前节点启动的DFS访问
  • 如果是一个环哪具体包含哪些节点 or 计算cycle的大小,可以加一个prev[cur] = prevNode来保存每一步怎么走的
  • def isCyclicDirectedGraph(graph: List[List[int]], n: int) -> bool:
        def dfs(cur) -> bool:  # cur启动的DFS中有没有环
            if recStack[cur]:  # 被当前node启动的dfs遍历过
                return True
            if visited[cur]:  # 被其他node启动的dfs遍历过
                return False
            visited[cur] = True
            recStack[cur] = True
            for next in graph[cur]:
                if dfs(next):
                    return True
            recStack[cur] = False  # backtrack
            return False
        visited = [False] * n
        recStack = [False] * n
        for i in range(n):
            if not visited[i] and dfs(i):
                return True
        return False
    

    2127. 参加会议的最多员工数(Hard)

    Solu:DFS + 双array

  • step 1: DFS+双array找最大环
  • 因为size = 2的cycles们可以拼在一桌说,所以要单独记下
  • step 2: DFS找size = 2的每个cycle中的两个node分别可以延长出的最长path是多少,把他们都拼到一桌上
  • step 3: res = max{最大环,所有size = 2的cycles及其extended paths的拼接}
  • Code:

    class Solution:
        def __init__(self):
            self.sizeTwoCycle = []
            self.maxCycle = 0
            self.favorite = None
        def maximumInvitations(self, favorite: List[int]) -> int:
            def buildGraph():
                beloved = {i: [] for i in range(len(favorite))}
                for i, fav in enumerate(favorite):  # 被谁喜欢
                    beloved[fav].append(i)
                return beloved
            beloved = buildGraph()
            self.favorite = favorite
            self.countCycle(beloved)
            print(self.maxCycle)
            # 尝试在size=2的cycle中,对两个node都尝试extend一条最长的path (NOT cycle)
            return max(self.maxCycle, self.sizeTwoExtend(beloved))
        def countCycle(self, beloved) -> None:  # find the max cycle size, and store all size=2 cycles
            def dfs(cur, cycleSize):
                if recStack[cur]:  # cycle found
                    self.maxCycle = max(self.maxCycle, cycleSize)
                    if cycleSize == 2:
                        self.sizeTwoCycle.append((cur, self.favorite[cur]))
                    return
                if visited[cur]:
                    return
                visited[cur] = True
                recStack[cur] = True
                for next in beloved[cur]:
                    dfs(next, cycleSize + 1)
                recStack[cur] = False  # backtracking
            visited = [False] * len(beloved)
            recStack = [False] * len(beloved)
            for i in range(len(beloved)):
                if not visited[i]:
                    dfs(i, 0)
        def sizeTwoExtend(self, beloved) -> int:  # extend the longest path from both two nodes in size=2 cycle
            def dfs(cur) -> int:  # longest path from cur
                if visited[cur]:
                    return 0
                res = 0
                for next in beloved[cur]:
                    res = max(res, dfs(next))
                return res + 1
            visited = [False] * len(beloved)
            res = 0
            for i, j in self.sizeTwoCycle:
                # handle i
                visited[j] = True
                iLongest = dfs(i)
                visited[j] = False  # backtracking
                # handle j
                visited[i] = True
                jLongest = dfs(j)
                visited[i] = False  # backtracking
                res += iLongest + jLongest  # 不会重复加上,因为每个人只喜欢一个人,所以不可能有同一个node出现在多个size=2 cycle中
            return res
    

    Reference:

    分类:
    代码人生
    标签: