From 7aaf79cc23c7bac95d4da830605341b68529dd47 Mon Sep 17 00:00:00 2001 From: Sanders Lin <45224617+SandersLin@users.noreply.github.com> Date: Sat, 18 Apr 2020 02:05:29 +0800 Subject: [PATCH] Initialize set with source in DFS (#1872) * Update dfs.py * Add type hints, rearrange doc-strings and comments * fixup! Format Python code with psf/black push * dfs -> depth_first_search Co-Authored-By: Christian Clauss * dfs -> depth_first_search * Add doctest for DFS * fixup! Format Python code with psf/black push * Rename dfs.py to depth_first_search_dictionary.py * updating DIRECTORY.md * Rename depth_first_search_dictionary.py to depth_first_search_dfs.py * updating DIRECTORY.md * Rename depth_first_search.py to depth_first_search_2.py * updating DIRECTORY.md * Rename depth_first_search_dfs.py to depth_first_search.py * updating DIRECTORY.md Co-authored-by: John Law Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 2 +- graphs/depth_first_search.py | 106 ++++++++++++++++----------------- graphs/depth_first_search_2.py | 65 ++++++++++++++++++++ graphs/dfs.py | 44 -------------- 4 files changed, 116 insertions(+), 101 deletions(-) create mode 100644 graphs/depth_first_search_2.py delete mode 100644 graphs/dfs.py diff --git a/DIRECTORY.md b/DIRECTORY.md index b582a8fb8..d1d10942f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -212,7 +212,7 @@ * [Check Bipartite Graph Bfs](https://github.com/TheAlgorithms/Python/blob/master/graphs/check_bipartite_graph_bfs.py) * [Check Bipartite Graph Dfs](https://github.com/TheAlgorithms/Python/blob/master/graphs/check_bipartite_graph_dfs.py) * [Depth First Search](https://github.com/TheAlgorithms/Python/blob/master/graphs/depth_first_search.py) - * [Dfs](https://github.com/TheAlgorithms/Python/blob/master/graphs/dfs.py) + * [Depth First Search 2](https://github.com/TheAlgorithms/Python/blob/master/graphs/depth_first_search_2.py) * [Dijkstra](https://github.com/TheAlgorithms/Python/blob/master/graphs/dijkstra.py) * [Dijkstra 2](https://github.com/TheAlgorithms/Python/blob/master/graphs/dijkstra_2.py) * [Dijkstra Algorithm](https://github.com/TheAlgorithms/Python/blob/master/graphs/dijkstra_algorithm.py) diff --git a/graphs/depth_first_search.py b/graphs/depth_first_search.py index 0593e120b..1206d5ae9 100644 --- a/graphs/depth_first_search.py +++ b/graphs/depth_first_search.py @@ -1,65 +1,59 @@ -#!/usr/bin/python +"""The DFS function simply calls itself recursively for every unvisited child of +its argument. We can emulate that behaviour precisely using a stack of iterators. +Instead of recursively calling with a node, we'll push an iterator to the node's +children onto the iterator stack. When the iterator at the top of the stack +terminates, we'll pop it off the stack. -""" Author: OMKAR PATHAK """ +Pseudocode: + all nodes initially unexplored + mark s as explored + for every edge (s, v): + if v unexplored: + DFS(G, v) +""" + +from typing import Set, Dict -class Graph: - def __init__(self): - self.vertex = {} +def depth_first_search(graph: Dict, start: str) -> Set[int]: + """Depth First Search on Graph - # for printing the Graph vertices - def printGraph(self): - print(self.vertex) - for i in self.vertex.keys(): - print(i, " -> ", " -> ".join([str(j) for j in self.vertex[i]])) + :param graph: directed graph in dictionary format + :param vertex: starting vectex as a string + :returns: the trace of the search + >>> G = { "A": ["B", "C", "D"], "B": ["A", "D", "E"], + ... "C": ["A", "F"], "D": ["B", "D"], "E": ["B", "F"], + ... "F": ["C", "E", "G"], "G": ["F"] } + >>> start = "A" + >>> output_G = list({'A', 'B', 'C', 'D', 'E', 'F', 'G'}) + >>> all(x in output_G for x in list(depth_first_search(G, "A"))) + True + >>> all(x in output_G for x in list(depth_first_search(G, "G"))) + True + """ + explored, stack = set(start), [start] + while stack: + v = stack.pop() + # one difference from BFS is to pop last element here instead of first one + for w in graph[v]: + if w not in explored: + explored.add(w) + stack.append(w) + return explored - # for adding the edge between two vertices - def addEdge(self, fromVertex, toVertex): - # check if vertex is already present, - if fromVertex in self.vertex.keys(): - self.vertex[fromVertex].append(toVertex) - else: - # else make a new vertex - self.vertex[fromVertex] = [toVertex] - - def DFS(self): - # visited array for storing already visited nodes - visited = [False] * len(self.vertex) - - # call the recursive helper function - for i in range(len(self.vertex)): - if visited[i] == False: - self.DFSRec(i, visited) - - def DFSRec(self, startVertex, visited): - # mark start vertex as visited - visited[startVertex] = True - - print(startVertex, end=" ") - - # Recur for all the vertices that are adjacent to this node - for i in self.vertex.keys(): - if visited[i] == False: - self.DFSRec(i, visited) +G = { + "A": ["B", "C", "D"], + "B": ["A", "D", "E"], + "C": ["A", "F"], + "D": ["B", "D"], + "E": ["B", "F"], + "F": ["C", "E", "G"], + "G": ["F"], +} if __name__ == "__main__": - g = Graph() - g.addEdge(0, 1) - g.addEdge(0, 2) - g.addEdge(1, 2) - g.addEdge(2, 0) - g.addEdge(2, 3) - g.addEdge(3, 3) + import doctest - g.printGraph() - print("DFS:") - g.DFS() - - # OUTPUT: - # 0  ->  1 -> 2 - # 1  ->  2 - # 2  ->  0 -> 3 - # 3  ->  3 - # DFS: - #  0 1 2 3 + doctest.testmod() + print(depth_first_search(G, "A")) diff --git a/graphs/depth_first_search_2.py b/graphs/depth_first_search_2.py new file mode 100644 index 000000000..0593e120b --- /dev/null +++ b/graphs/depth_first_search_2.py @@ -0,0 +1,65 @@ +#!/usr/bin/python + +""" Author: OMKAR PATHAK """ + + +class Graph: + def __init__(self): + self.vertex = {} + + # for printing the Graph vertices + def printGraph(self): + print(self.vertex) + for i in self.vertex.keys(): + print(i, " -> ", " -> ".join([str(j) for j in self.vertex[i]])) + + # for adding the edge between two vertices + def addEdge(self, fromVertex, toVertex): + # check if vertex is already present, + if fromVertex in self.vertex.keys(): + self.vertex[fromVertex].append(toVertex) + else: + # else make a new vertex + self.vertex[fromVertex] = [toVertex] + + def DFS(self): + # visited array for storing already visited nodes + visited = [False] * len(self.vertex) + + # call the recursive helper function + for i in range(len(self.vertex)): + if visited[i] == False: + self.DFSRec(i, visited) + + def DFSRec(self, startVertex, visited): + # mark start vertex as visited + visited[startVertex] = True + + print(startVertex, end=" ") + + # Recur for all the vertices that are adjacent to this node + for i in self.vertex.keys(): + if visited[i] == False: + self.DFSRec(i, visited) + + +if __name__ == "__main__": + g = Graph() + g.addEdge(0, 1) + g.addEdge(0, 2) + g.addEdge(1, 2) + g.addEdge(2, 0) + g.addEdge(2, 3) + g.addEdge(3, 3) + + g.printGraph() + print("DFS:") + g.DFS() + + # OUTPUT: + # 0  ->  1 -> 2 + # 1  ->  2 + # 2  ->  0 -> 3 + # 3  ->  3 + # DFS: + #  0 1 2 3 diff --git a/graphs/dfs.py b/graphs/dfs.py deleted file mode 100644 index f183eae73..000000000 --- a/graphs/dfs.py +++ /dev/null @@ -1,44 +0,0 @@ -"""pseudo-code""" - -""" -DFS(graph G, start vertex s): -// all nodes initially unexplored -mark s as explored -for every edge (s, v): - if v unexplored: - DFS(G, v) -""" - - -def dfs(graph, start): - """The DFS function simply calls itself recursively for every unvisited child of its argument. We can emulate that - behaviour precisely using a stack of iterators. Instead of recursively calling with a node, we'll push an iterator - to the node's children onto the iterator stack. When the iterator at the top of the stack terminates, we'll pop - it off the stack.""" - explored, stack = set(), [start] - while stack: - v = ( - stack.pop() - ) # one difference from BFS is to pop last element here instead of first one - - if v in explored: - continue - - explored.add(v) - - for w in graph[v]: - if w not in explored: - stack.append(w) - return explored - - -G = { - "A": ["B", "C"], - "B": ["A", "D", "E"], - "C": ["A", "F"], - "D": ["B"], - "E": ["B", "F"], - "F": ["C", "E"], -} - -print(dfs(G, "A"))