tests for the original code

This commit is contained in:
yashwanth-adimulam 2024-10-26 10:42:23 +05:30
parent 7d1575d3bf
commit fdbf9b12e5

View File

@ -10,23 +10,17 @@ Useful Links: https://www.geeksforgeeks.org/applications-advantages-and-disadvan
https://en.wikipedia.org/wiki/Graph_(discrete_mathematics) https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)
""" """
from collections import deque from collections import deque
from math import floor from math import floor
from random import random from random import random
from time import time from time import time
class DirectedGraph: class DirectedGraph:
def __init__(self) -> None: def __init__(self):
"""
Initialize a directed graph.
>>> g = DirectedGraph()
>>> g.all_nodes()
[]
"""
self.graph = {} self.graph = {}
def add_pair(self, u: int, v: int, w: int = 1) -> None: def add_pair(self, u, v, w=1):
""" """
Add a directed edge from u to v with weight w. Add a directed edge from u to v with weight w.
@ -36,14 +30,14 @@ class DirectedGraph:
{1: [[3, 2]], 2: []} {1: [[3, 2]], 2: []}
""" """
if self.graph.get(u): if self.graph.get(u):
if not any(edge[1] == v and edge[0] == w for edge in self.graph[u]): if self.graph[u].count([w, v]) == 0:
self.graph[u].append([w, v]) self.graph[u].append([w, v])
else: else:
self.graph[u] = [[w, v]] self.graph[u] = [[w, v]]
if v not in self.graph: if not self.graph.get(v):
self.graph[v] = [] self.graph[v] = []
def all_nodes(self) -> list: def all_nodes(self):
""" """
Return a list of all nodes in the graph. Return a list of all nodes in the graph.
@ -54,7 +48,7 @@ class DirectedGraph:
""" """
return list(self.graph) return list(self.graph)
def remove_pair(self, u: int, v: int) -> None: def remove_pair(self, u, v):
""" """
Remove the directed edge from u to v. Remove the directed edge from u to v.
@ -65,9 +59,11 @@ class DirectedGraph:
{1: [], 2: []} {1: [], 2: []}
""" """
if self.graph.get(u): if self.graph.get(u):
self.graph[u] = [edge for edge in self.graph[u] if edge[1] != v] for _ in self.graph[u]:
if _[1] == v:
self.graph[u].remove(_)
def dfs(self, s: int = -2, d: int = -1) -> list: def dfs(self, s=-2, d=-1):
""" """
Perform depth-first search from node s to d. Perform depth-first search from node s to d.
@ -76,6 +72,8 @@ class DirectedGraph:
>>> g.add_pair(2, 3) >>> g.add_pair(2, 3)
>>> g.dfs(1, 3) >>> g.dfs(1, 3)
[1, 2, 3] [1, 2, 3]
>>> g.dfs(1, 2)
[1, 2]
""" """
if s == d: if s == d:
return [] return []
@ -85,23 +83,48 @@ class DirectedGraph:
s = next(iter(self.graph)) s = next(iter(self.graph))
stack.append(s) stack.append(s)
visited.append(s) visited.append(s)
while stack: while True:
current = stack[-1] if len(self.graph[s]) != 0:
if current == d: ss = s
for node in self.graph[s]:
if visited.count(node[1]) < 1:
if node[1] == d:
visited.append(d)
return visited return visited
if current in self.graph: else:
for edge in self.graph[current]: stack.append(node[1])
if edge[1] not in visited: visited.append(node[1])
stack.append(edge[1]) ss = node[1]
visited.append(edge[1])
break break
else:
if s == ss:
stack.pop() stack.pop()
if len(stack) != 0:
s = stack[len(stack) - 1]
else: else:
stack.pop() s = ss
if len(stack) == 0:
return visited return visited
def bfs(self, s: int = -2) -> list: def fill_graph_randomly(self, c=-1):
"""
Fill the graph with random edges.
>>> g = DirectedGraph()
>>> g.fill_graph_randomly(5)
>>> len(g.all_nodes()) > 0
True
"""
if c == -1:
c = floor(random() * 10000) + 10
for i in range(c):
for _ in range(floor(random() * 102) + 1):
n = floor(random() * c) + 1
if n != i:
self.add_pair(i, n, 1)
def bfs(self, s=-2):
""" """
Perform breadth-first search starting from node s. Perform breadth-first search starting from node s.
@ -118,15 +141,134 @@ class DirectedGraph:
d.append(s) d.append(s)
visited.append(s) visited.append(s)
while d: while d:
current = d.popleft() s = d.popleft()
if current in self.graph: if len(self.graph[s]) != 0:
for edge in self.graph[current]: for node in self.graph[s]:
if edge[1] not in visited: if visited.count(node[1]) < 1:
d.append(edge[1]) d.append(node[1])
visited.append(edge[1]) visited.append(node[1])
return visited return visited
def has_cycle(self) -> bool: def in_degree(self, u):
"""
Calculate in-degree of node u.
>>> g = DirectedGraph()
>>> g.add_pair(1, 2)
>>> g.in_degree(2)
1
"""
count = 0
for x in self.graph:
for y in self.graph[x]:
if y[1] == u:
count += 1
return count
def out_degree(self, u):
"""
Calculate out-degree of node u.
>>> g = DirectedGraph()
>>> g.add_pair(1, 2)
>>> g.out_degree(1)
1
"""
return len(self.graph[u])
def topological_sort(self, s=-2):
"""
Perform topological sort of the graph.
>>> g = DirectedGraph()
>>> g.add_pair(1, 2)
>>> g.add_pair(2, 3)
>>> g.topological_sort()
[1, 2, 3]
"""
stack = []
visited = []
if s == -2:
s = next(iter(self.graph))
stack.append(s)
visited.append(s)
sorted_nodes = []
while True:
if len(self.graph[s]) != 0:
ss = s
for node in self.graph[s]:
if visited.count(node[1]) < 1:
stack.append(node[1])
visited.append(node[1])
ss = node[1]
break
if s == ss:
sorted_nodes.append(stack.pop())
if len(stack) != 0:
s = stack[len(stack) - 1]
else:
s = ss
if len(stack) == 0:
return sorted_nodes
def cycle_nodes(self):
"""
Get nodes that are part of a cycle.
>>> g = DirectedGraph()
>>> g.add_pair(1, 2)
>>> g.add_pair(2, 1)
>>> g.cycle_nodes()
[1, 2]
"""
stack = []
visited = []
s = next(iter(self.graph))
stack.append(s)
visited.append(s)
parent = -2
indirect_parents = []
ss = s
anticipating_nodes = set()
while True:
if len(self.graph[s]) != 0:
ss = s
for node in self.graph[s]:
if (
visited.count(node[1]) > 0
and node[1] != parent
and indirect_parents.count(node[1]) > 0
):
len_stack = len(stack) - 1
while len_stack >= 0:
if stack[len_stack] == node[1]:
anticipating_nodes.add(node[1])
break
else:
anticipating_nodes.add(stack[len_stack])
len_stack -= 1
if visited.count(node[1]) < 1:
stack.append(node[1])
visited.append(node[1])
ss = node[1]
break
if s == ss:
stack.pop()
if len(stack) != 0:
s = stack[len(stack) - 1]
else:
parent = s
s = ss
if len(stack) == 0:
return list(anticipating_nodes)
def has_cycle(self):
""" """
Check if the graph has a cycle. Check if the graph has a cycle.
@ -136,73 +278,65 @@ class DirectedGraph:
>>> g.has_cycle() >>> g.has_cycle()
True True
""" """
visited = set() stack = []
rec_stack = set() visited = []
s = next(iter(self.graph))
stack.append(s)
visited.append(s)
parent = -2
indirect_parents = []
ss = s
def cycle_util(v): while True:
visited.add(v) if len(self.graph[s]) != 0:
rec_stack.add(v) ss = s
for edge in self.graph.get(v, []): for node in self.graph[s]:
if edge[1] not in visited: if (
if cycle_util(edge[1]): visited.count(node[1]) > 0
and node[1] != parent
and indirect_parents.count(node[1]) > 0
):
return True return True
elif edge[1] in rec_stack: if visited.count(node[1]) < 1:
return True stack.append(node[1])
rec_stack.remove(v) visited.append(node[1])
ss = node[1]
break
if s == ss:
stack.pop()
if len(stack) != 0:
s = stack[len(stack) - 1]
else:
s = ss
if len(stack) == 0:
return False return False
for node in self.graph: def dfs_time(self, s=-2, e=-1):
if node not in visited:
if cycle_util(node):
return True
return False
# Additional methods would go here with doctests...
class Graph:
def __init__(self) -> None:
""" """
Initialize an undirected graph. Measure the time taken for DFS.
>>> g = Graph() >>> g = DirectedGraph()
>>> g.all_nodes()
[]
"""
self.graph = {}
def add_pair(self, u: int, v: int, w: int = 1) -> None:
"""
Add an undirected edge between u and v with weight w.
>>> g = Graph()
>>> g.add_pair(1, 2, 3)
>>> g.graph
{1: [[3, 2]], 2: [[3, 1]]}
"""
if self.graph.get(u):
if not any(edge[1] == v and edge[0] == w for edge in self.graph[u]):
self.graph[u].append([w, v])
else:
self.graph[u] = [[w, v]]
if self.graph.get(v):
if not any(edge[1] == u and edge[0] == w for edge in self.graph[v]):
self.graph[v].append([w, u])
else:
self.graph[v] = [[w, u]]
def all_nodes(self) -> list:
"""
Return a list of all nodes in the graph.
>>> g = Graph()
>>> g.add_pair(1, 2) >>> g.add_pair(1, 2)
>>> g.all_nodes() >>> g.dfs_time(1, 2) >= 0
[1, 2] True
""" """
return list(self.graph) begin = time()
self.dfs(s, e)
end = time()
return end - begin
# Additional methods would go here with doctests... def bfs_time(self, s=-2):
"""
Measure the time taken for BFS.
if __name__ == "__main__": >>> g = DirectedGraph()
import doctest >>> g.add_pair(1, 2)
doctest.testmod() >>> g.bfs_time(1) >= 0
True
"""
begin = time()
self.bfs(s)
end = time()
return end - begin