forked from techqueria/data-structures-and-algorithms
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmiguelHx.py
More file actions
180 lines (152 loc) · 5.02 KB
/
miguelHx.py
File metadata and controls
180 lines (152 loc) · 5.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
"""Python Version 3.9.2
4.1 - Route Between Nodes:
Given a directed graph, design an algorithm to
find out whether there is a route between two nodes.
"""
import unittest
from collections import deque
from dataclasses import dataclass
from typing import List, Deque, Set
@dataclass
class Graph:
nodes: 'List[Node]'
def print_graph(self):
for node in self.nodes:
node.print_children()
@dataclass
class Node:
id: int
children: 'List[Node]'
def add_child(self, *nodes: 'Node'):
for node in nodes:
self.children.append(node)
def children_as_str(self) -> str:
return ', '.join(str(child.id) for child in self.children)
def print_children(self):
logging.debug('Adjacency list for node %s: %s', self.id, self.children_as_str())
def __str__(self):
return f'Node ({self.id}), children: {self.children_as_str()}'
def bfs_search_exhaustive(root: Node) -> List[int]:
"""Simple BFS.
takes in a root, returns a list
of ids of the sequence of visited
nodes. Goes through entire graph.
Args:
root (Node): starting node
Returns:
List[int]: List[int]: list of node IDs (i.e. [0, 1, 4])
"""
visited_list: List[int] = [root.id]
visited: Set[int] = set([root.id])
queue: Deque[Node] = deque([root])
while queue:
node = queue.popleft()
# print(f'Visiting node ({node.id})')
for n in node.children:
if n.id not in visited:
queue.append(n)
visited_list.append(n.id)
visited.add(n.id)
return visited_list
def bfs_search_for_dest(root: Node, dest: Node) -> List[int]:
"""Simple BFS.
takes in a root, returns a list
of ids of the sequence of visited
nodes. Stops at destination node
Args:
root (Node): starting node
Returns:
List[int]: List[int]: list of node IDs (i.e. [0, 1, 4])
"""
visited_list: List[int] = [root.id]
visited: Set[int] = set([root.id])
queue: Deque[Node] = deque([root])
while queue:
node = queue.popleft()
# print(f'Visiting node ({node.id})')
for n in node.children:
if n.id not in visited:
queue.append(n)
visited_list.append(n.id)
visited.add(n.id)
if n.id == dest.id:
# done searching
return visited_list
return visited_list
def route_between_nodes(src: Node, dest: Node) -> bool:
"""This function will return true if a path
is found between two nodes, false otherwise.
The idea is to perform a breadth first search
from src to dest. After obtaining a list of
nodes visited, we simply check to see if destination
node id is in there.
Runtime Complexity:
O(V + E) where V represents the number of
nodes in the graph and E represents the number
of edges in this graph.
Space Complexity:
O(V) where V represents the number of existing nodes
in the graph.
Args:
src (Node): from node
dest (Node): destination node
Returns:
bool: whether a path between src and dest exists
"""
ids_visited: List[int] = bfs_search_for_dest(src, dest)
return dest.id in ids_visited
class TestRouteBetweenNodes(unittest.TestCase):
def test_route_between_nodes(self):
n0 = Node(0, [])
n1 = Node(1, [])
n2 = Node(2, [])
n3 = Node(3, [])
n4 = Node(4, [])
n5 = Node(5, [])
n0.add_child(n1, n4, n5)
n1.add_child(n3, n4)
n2.add_child(n1)
n3.add_child(n2, n4)
# must remember to reset node visited properties
# before each fresh run
g = Graph([n0, n1, n2, n3, n4, n5])
# There is a route from node 0 to node 2
self.assertTrue(route_between_nodes(n0, n2))
# No route between node 1 and node 0
self.assertFalse(route_between_nodes(n1, n0))
# There is a route from node 2 to node 3
self.assertTrue(route_between_nodes(n2, n3))
class TestMyGraphSearch(unittest.TestCase):
def test_basic_graph_creation(self):
n0 = Node(0, [])
n1 = Node(1, [])
n2 = Node(2, [])
n3 = Node(3, [])
n4 = Node(4, [])
n5 = Node(5, [])
n6 = Node(6, [])
n0.add_child(n1)
n1.add_child(n2)
n2.add_child(n0, n3)
n3.add_child(n2)
n4.add_child(n6)
n5.add_child(n4)
n6.add_child(n5)
nodes = [n0, n1, n2, n3, n4, n5, n6]
g = Graph(nodes)
# g.print_graph()
def test_basic_breadth_first_search_exhaustive(self):
n0 = Node(0, [])
n1 = Node(1, [])
n2 = Node(2, [])
n3 = Node(3, [])
n4 = Node(4, [])
n5 = Node(5, [])
n0.add_child(n1, n4, n5)
n1.add_child(n3, n4)
n2.add_child(n1)
n3.add_child(n2, n4)
result: List[int] = bfs_search_exhaustive(n0)
self.assertEqual(result, [0, 1, 4, 5, 3, 2])
if __name__ == '__main__':
unittest.main()