Leetcode0802. Find the final safe state (medium, DFS, topological sort)

content

1. Topic description

2. Method 1: Depth-first search

2.1 Ideas

2.2 Code Implementation

3. Method 2: Depth-first search + three-color marking method

3.1 Ideas

3.2 Code

4. Method 3: Topological Sort


1. Topic description

There is a  n directed graph with nodes, and the nodes are  0 numbered  n - 1 . The graph is represented by a   2D array of integers  indexed from 0graph , which  graph[i]is an integer array of nodes  i adjacent to a node, which means that there is an edge from a node  i to  graph[i]each node in .

A node is a terminal node if it has no outgoing directed edges   . If there are no outgoing edges, the node is a terminal node.  A node is a  secure node if all possible paths from it lead to a  terminal node  .

Returns an   array of all safe nodes in the graph as the answer. The elements in the answer array should be in  ascending  order.

Example 1:

Input: graph = [[1,2],[2,3],[5],[0],[5],[],[]]
 Output: [2,4,5,6]
 Explanation: The diagram is as above .
Node 5 and Node 6 are terminal nodes because neither of them have outgoing edges.
All paths from nodes 2, 4, 5 and 6 lead to node 5 or 6.

Example 2:

Input: graph = [[1,2,3,4],[1,2],[3,4],[0,4],[]]
 Output: [4]
 Explanation:
Only node 4 is a terminal node, and all paths from node 4 lead to node 4.

hint:

  • n == graph.length
  • 1 <= n <= 10^4
  • 0 <= graph[i].length <= n
  • graph[i] Arranged in strictly increasing order.
  • The graph may contain self-loops.
  • The number of edges in the graph is in range  [1, 4 * 10^4] .

Source: LeetCode
Link: https://leetcode-cn.com/problems/find-eventual-safe-states The
copyright belongs to LeetCode.com. For commercial reprints, please contact the official authorization, and for non-commercial reprints, please indicate the source.

2. Method 1: Depth-first search

2.1 Ideas

        The 2-dimensional array graph is the representation of the adjacency list of the input graph.

        According to the meaning of the question, the terminal node itself is also a security node.

        

        First of all, it is easy to determine the terminal node according to the graph. Since it is a directed graph, if graph[i] is empty, it means that it is a terminal node.

        Do a depth-first traversal of each non-terminal node node_j to see the number of distinct terminal nodes it can reach. If it is 1 then it is a secure node, otherwise it is a non-secure node.        

        If a node is not a secure node, then the nodes that can reach this node are not secure nodes. But the reverse is not true, that is, a node is a secure node, but it does not mean that the nodes that can reach it are also secure nodes. For example, in the above example 1, node 2 is a secure node, and node 1 can reach 2, but it also forms a loop with node 3 and node 0, so it is not a secure node.

        It is obvious that if there is a cycle (including self-loop) in the graph, all nodes in the cycle are not terminal nodes, and this ring is one of their common ring paths, so they are not safe nodes.

        Therefore, the individual nodes can be traversed in the following way, for each node a depth-first path traversal is performed:

  • (1) Depth-first traversal from a non-terminal node has the following results:
    • (a) A non-secure node is reached on the way, so it belongs to a non-secure node, and it is marked as a non-secure node
    • (b) can only reach one terminal node, so it is a secure node, mark it as a secure node
    • (c) Multiple terminal nodes can be reached, but are not within any loop and therefore are not safe nodes, mark them as safe nodes
    • (d) exists in a ring, so it is not a safe node, and all nodes in the ring are not safe nodes, and all nodes on the ring are marked as non-safe nodes
  • (2) Repeat step (1) until all nodes have completed the secure and non-secure classification

See Leetcode0797. All possible paths (medium, DFS)         for depth-first path search in directed acyclic graphs (DAGs ) . This topic is directed graph but there may be loops, so one more loop judgment processing is needed.

        Commit error after initial implementation: [[],[0,2,3,4],[3],[4],[]], expected output is [0,1,2,3,4]. I checked the code repeatedly and confirmed it by hand, and I felt that it was impossible to be wrong. . . Did I catch a bug in leetcode? tan(90)! I looked at the solutions submitted by others, and saw that some people said that they had read the wrong title, and then read the title again, it is true. . . "All possible paths lead to a  terminal node ", this description is too confusing, my first impression is that all paths lead to the same terminal node , cry with laughter. In this way, the situation becomes simpler. The above processing steps are revised as follows:

  • (1) Depth-first traversal from a non-terminal node has the following results:
    • (a) A non-secure node is reached on the way, so it belongs to a non-secure node, and it is marked as a non-secure node
    • (b) exists in a ring, so it is not a safe node, and all nodes in the ring are not safe nodes, and all nodes on the ring are marked as non-safe nodes
    • (c) each path ends at a terminal node and is therefore a safe node, mark it as a safe node
  • (2) Repeat step (1) until all nodes have completed the secure and non-secure classification

2.2 Code Implementation

from typing import List

class Solution:
    def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
        # print(graph)
        endnodes = set()
        secure   = set()
        insecure = set()
        for k in range(len(graph)):
            if len(graph[k])==0:
                secure.add(k)
                endnodes.add(k)
            elif k in graph[k]:
                # self-loop
                insecure.add(k)
            
        def dfs(path)->bool:
            # ret: Whether it is judged as insecure. 
            #      Even True, it is still not clear whether secure
            
            # print('dfs(): ', path, secure,insecure)
            # Traverse all neighbor nodes of the last node
            is_insecure = False
            for node in graph[path[-1]]:
                # print('\t\t', node)
                if node in insecure:
                    insecure.add(path[0])
                    return True
                elif node in path:
                    # loop found
                    insecure.update(set(path))
                    return True
                elif node in endnodes:
                    # secure.add(path[0])
                    # return 
                    continue
                else:
                    is_insecure = is_insecure or dfs(path+[node])
            return is_insecure

        for n in range(len(graph)):
            if (n not in insecure) and (n not in secure):
                if not dfs([n]):
                    secure.add(n)
                else:
                    insecure.add(n)                
        return sorted(list(secure))
                
if __name__ == "__main__":
    
    sln = Solution()
    
    graph = [[1,2],[2,3],[5],[0],[5],[],[]]
    print(sln.eventualSafeNodes(graph))
    
    graph = [[1,2,3,4],[1,2],[3,4],[0,4],[]]
    print(sln.eventualSafeNodes(graph))
    
    graph = [[],[0,2,3,4],[3],[4],[]]
    print(sln.eventualSafeNodes(graph))

        Sadly timed out. 

3. Method 2: Depth-first search + three-color marking method

3.1 Ideas

        Since it is not required that all paths of a secure node end in the same node, any node that is not in the ring is actually a secure node. So the problem reduces to the existence of cycles in the search graph. You can use depth-first search to find rings, and mark nodes with three colors during depth-first search. The rules for marking are as follows:

  • white (represented by 0): the node has not been visited;
  • Gray (indicated by 1): the node is on the recursion stack, or on a ring;
  • Black (represented by 2): The node has been searched and is a safe node.

        When we visit a node for the first time, we mark it gray and continue searching for nodes connected to it.

        If a gray node is encountered during the search process, it means that a ring has been found, and the search is exited at this time, and the nodes in the stack remain gray. on all nodes.

        If no gray node is encountered during the search process, it means that no ring is encountered, then before recursively returning, we change its mark from gray to black, which means it is a safe node.

3.2 Code

class Solution:
    def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
        # print(graph)
        
        flag = len(graph)*[0]
        
        def dfs(x)->bool:
            if flag[x]>0:
                # this node has already been visited
                return flag[x] == 2
            flag[x] = 1 # flagged as visited
            for neighbour in graph[x]:
                if not dfs(neighbour):
                    return False
            flag[x] = 2
            return True
        
        return [k for k in range(len(graph)) if dfs(k)]

        Execution time: 116 ms, beating 70.22% of users across all Python3 commits

        Memory consumption: 20.7 MB, beating 25.21% of users across all Python3 commits

4. Method 3: Topological Sort

        The official solution also gives a solution based on topological sorting. The idea is excerpted as follows, and the code is omitted.

        According to the meaning of the question, if a node has no outgoing edge, the node is safe; if the points connected to the outgoing edge of a node are all safe, then the node is also safe.

        According to this property, we can reverse all edges in the graph, get an inverse graph, and then run topological sort on the inverse graph.

        Specifically, first get the inverse graph rgand its in-degree array inDeg. Add all the points whose in-degree is 0 to the queue, and then continuously take out the first element of the queue, and reduce the in-degree of the point connected to its out-edge by one. Loop until the queue is empty. After the loop ends, all nodes with an in-degree of 0 are safe. We iterate over the in-degree array and add points with in-degree 0 to the answer list.

        Back to the main directory: Leetcode daily problem-solving notes (dynamic update...)

Guess you like

Origin blog.csdn.net/chenxy_bwave/article/details/124209742