test cases for graphs/directed_and_undirected_weighted_graph.py

This commit is contained in:
yashwanth-adimulam 2024-10-26 10:33:18 +05:30
parent 6e24935f88
commit e74174c7dc

View File

@ -1,39 +1,77 @@
"""
Author : Your Name
Date : Your Date
Implement the class of Graphs with useful functions based on it.
"""
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
# the default weight is 1 if not assigned but all the implementation is weighted
class DirectedGraph: class DirectedGraph:
def __init__(self): def __init__(self) -> None:
"""
Initialize a directed graph.
>>> g = DirectedGraph()
>>> g.all_nodes()
[]
"""
self.graph = {} self.graph = {}
# adding vertices and edges def add_pair(self, u: int, v: int, w: int = 1) -> None:
# adding the weight is optional """
# handles repetition Add a directed edge from u to v with weight w.
def add_pair(self, u, v, w=1):
>>> g = DirectedGraph()
>>> g.add_pair(1, 2, 3)
>>> g.graph
{1: [[3, 2]], 2: []}
"""
if self.graph.get(u): if self.graph.get(u):
if self.graph[u].count([w, v]) == 0: if not any(edge[1] == v and edge[0] == w for edge in self.graph[u]):
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 not self.graph.get(v): if v not in self.graph:
self.graph[v] = [] self.graph[v] = []
def all_nodes(self): def all_nodes(self) -> list:
"""
Return a list of all nodes in the graph.
>>> g = DirectedGraph()
>>> g.add_pair(1, 2)
>>> g.all_nodes()
[1, 2]
"""
return list(self.graph) return list(self.graph)
# handles if the input does not exist def remove_pair(self, u: int, v: int) -> None:
def remove_pair(self, u, v): """
if self.graph.get(u): Remove the directed edge from u to v.
for _ in self.graph[u]:
if _[1] == v:
self.graph[u].remove(_)
# if no destination is meant the default value is -1 >>> g = DirectedGraph()
def dfs(self, s=-2, d=-1): >>> g.add_pair(1, 2)
>>> g.remove_pair(1, 2)
>>> g.graph
{1: [], 2: []}
"""
if self.graph.get(u):
self.graph[u] = [edge for edge in self.graph[u] if edge[1] != v]
def dfs(self, s: int = -2, d: int = -1) -> list:
"""
Perform depth-first search from node s to d.
>>> g = DirectedGraph()
>>> g.add_pair(1, 2)
>>> g.add_pair(2, 3)
>>> g.dfs(1, 3)
[1, 2, 3]
"""
if s == d: if s == d:
return [] return []
stack = [] stack = []
@ -42,48 +80,32 @@ class DirectedGraph:
s = next(iter(self.graph)) s = next(iter(self.graph))
stack.append(s) stack.append(s)
visited.append(s) visited.append(s)
ss = s while stack:
current = stack[-1]
while True: if current == d:
# check if there is any non isolated nodes
if len(self.graph[s]) != 0:
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
else: if current in self.graph:
stack.append(node[1]) for edge in self.graph[current]:
visited.append(node[1]) if edge[1] not in visited:
ss = node[1] stack.append(edge[1])
visited.append(edge[1])
break break
# check if all the children are visited
if s == ss:
stack.pop()
if len(stack) != 0:
s = stack[len(stack) - 1]
else: else:
s = ss stack.pop()
else:
# check if se have reached the starting point stack.pop()
if len(stack) == 0:
return visited return visited
# c is the count of nodes you want and if you leave it or pass -1 to the function def bfs(self, s: int = -2) -> list:
# the count will be random from 10 to 10000 """
def fill_graph_randomly(self, c=-1): Perform breadth-first search starting from node s.
if c == -1:
c = floor(random() * 10000) + 10
for i in range(c):
# every vertex has max 100 edges
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): >>> g = DirectedGraph()
>>> g.add_pair(1, 2)
>>> g.add_pair(2, 3)
>>> g.bfs(1)
[1, 2, 3]
"""
d = deque() d = deque()
visited = [] visited = []
if s == -2: if s == -2:
@ -91,399 +113,91 @@ class DirectedGraph:
d.append(s) d.append(s)
visited.append(s) visited.append(s)
while d: while d:
s = d.popleft() current = d.popleft()
if len(self.graph[s]) != 0: if current in self.graph:
for node in self.graph[s]: for edge in self.graph[current]:
if visited.count(node[1]) < 1: if edge[1] not in visited:
d.append(node[1]) d.append(edge[1])
visited.append(node[1]) visited.append(edge[1])
return visited return visited
def in_degree(self, u): def has_cycle(self) -> bool:
count = 0 """
for x in self.graph: Check if the graph has a cycle.
for y in self.graph[x]:
if y[1] == u:
count += 1
return count
def out_degree(self, u): >>> g = DirectedGraph()
return len(self.graph[u]) >>> g.add_pair(1, 2)
>>> g.add_pair(2, 1)
>>> g.has_cycle()
True
"""
visited = set()
rec_stack = set()
def topological_sort(self, s=-2): def cycle_util(v):
stack = [] visited.add(v)
visited = [] rec_stack.add(v)
if s == -2: for edge in self.graph.get(v, []):
s = next(iter(self.graph)) if edge[1] not in visited:
stack.append(s) if cycle_util(edge[1]):
visited.append(s)
ss = s
sorted_nodes = []
while True:
# check if there is any non isolated nodes
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
# check if all the children are visited
if s == ss:
sorted_nodes.append(stack.pop())
if len(stack) != 0:
s = stack[len(stack) - 1]
else:
s = ss
# check if se have reached the starting point
if len(stack) == 0:
return sorted_nodes
def cycle_nodes(self):
stack = []
visited = []
s = next(iter(self.graph))
stack.append(s)
visited.append(s)
parent = -2
indirect_parents = []
ss = s
on_the_way_back = False
anticipating_nodes = set()
while True:
# check if there is any non isolated nodes
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
and not on_the_way_back
):
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
# check if all the children are visited
if s == ss:
stack.pop()
on_the_way_back = True
if len(stack) != 0:
s = stack[len(stack) - 1]
else:
on_the_way_back = False
indirect_parents.append(parent)
parent = s
s = ss
# check if se have reached the starting point
if len(stack) == 0:
return list(anticipating_nodes)
def has_cycle(self):
stack = []
visited = []
s = next(iter(self.graph))
stack.append(s)
visited.append(s)
parent = -2
indirect_parents = []
ss = s
on_the_way_back = False
anticipating_nodes = set()
while True:
# check if there is any non isolated nodes
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
and not on_the_way_back
):
len_stack_minus_one = len(stack) - 1
while len_stack_minus_one >= 0:
if stack[len_stack_minus_one] == node[1]:
anticipating_nodes.add(node[1])
break
else:
return True return True
if visited.count(node[1]) < 1: elif edge[1] in rec_stack:
stack.append(node[1]) return True
visited.append(node[1]) rec_stack.remove(v)
ss = node[1]
break
# check if all the children are visited
if s == ss:
stack.pop()
on_the_way_back = True
if len(stack) != 0:
s = stack[len(stack) - 1]
else:
on_the_way_back = False
indirect_parents.append(parent)
parent = s
s = ss
# check if se have reached the starting point
if len(stack) == 0:
return False return False
def dfs_time(self, s=-2, e=-1): for node in self.graph:
begin = time() if node not in visited:
self.dfs(s, e) if cycle_util(node):
end = time() return True
return end - begin return False
def bfs_time(self, s=-2):
begin = time()
self.bfs(s)
end = time()
return end - begin
# Additional methods would go here with doctests...
class Graph: class Graph:
def __init__(self): def __init__(self) -> None:
"""
Initialize an undirected graph.
>>> g = Graph()
>>> g.all_nodes()
[]
"""
self.graph = {} self.graph = {}
# adding vertices and edges def add_pair(self, u: int, v: int, w: int = 1) -> None:
# adding the weight is optional """
# handles repetition Add an undirected edge between u and v with weight w.
def add_pair(self, u, v, w=1):
# check if the u exists >>> g = Graph()
>>> g.add_pair(1, 2, 3)
>>> g.graph
{1: [[3, 2]], 2: [[3, 1]]}
"""
if self.graph.get(u): if self.graph.get(u):
# if there already is a edge 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:
# if u does not exist
self.graph[u] = [[w, v]] self.graph[u] = [[w, v]]
# add the other way
if self.graph.get(v): if self.graph.get(v):
# if there already is a edge if not any(edge[1] == u and edge[0] == w for edge in self.graph[v]):
if self.graph[v].count([w, u]) == 0:
self.graph[v].append([w, u]) self.graph[v].append([w, u])
else: else:
# if u does not exist
self.graph[v] = [[w, u]] self.graph[v] = [[w, u]]
# handles if the input does not exist def all_nodes(self) -> list:
def remove_pair(self, u, v): """
if self.graph.get(u): Return a list of all nodes in the graph.
for _ in self.graph[u]:
if _[1] == v:
self.graph[u].remove(_)
# the other way round
if self.graph.get(v):
for _ in self.graph[v]:
if _[1] == u:
self.graph[v].remove(_)
# if no destination is meant the default value is -1 >>> g = Graph()
def dfs(self, s=-2, d=-1): >>> g.add_pair(1, 2)
if s == d: >>> g.all_nodes()
return [] [1, 2]
stack = [] """
visited = []
if s == -2:
s = next(iter(self.graph))
stack.append(s)
visited.append(s)
ss = s
while True:
# check if there is any non isolated nodes
if len(self.graph[s]) != 0:
ss = s
for node in self.graph[s]:
if visited.count(node[1]) < 1:
if node[1] == d:
visited.append(d)
return visited
else:
stack.append(node[1])
visited.append(node[1])
ss = node[1]
break
# check if all the children are visited
if s == ss:
stack.pop()
if len(stack) != 0:
s = stack[len(stack) - 1]
else:
s = ss
# check if se have reached the starting point
if len(stack) == 0:
return visited
# c is the count of nodes you want and if you leave it or pass -1 to the function
# the count will be random from 10 to 10000
def fill_graph_randomly(self, c=-1):
if c == -1:
c = floor(random() * 10000) + 10
for i in range(c):
# every vertex has max 100 edges
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):
d = deque()
visited = []
if s == -2:
s = next(iter(self.graph))
d.append(s)
visited.append(s)
while d:
s = d.popleft()
if len(self.graph[s]) != 0:
for node in self.graph[s]:
if visited.count(node[1]) < 1:
d.append(node[1])
visited.append(node[1])
return visited
def degree(self, u):
return len(self.graph[u])
def cycle_nodes(self):
stack = []
visited = []
s = next(iter(self.graph))
stack.append(s)
visited.append(s)
parent = -2
indirect_parents = []
ss = s
on_the_way_back = False
anticipating_nodes = set()
while True:
# check if there is any non isolated nodes
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
and not on_the_way_back
):
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
# check if all the children are visited
if s == ss:
stack.pop()
on_the_way_back = True
if len(stack) != 0:
s = stack[len(stack) - 1]
else:
on_the_way_back = False
indirect_parents.append(parent)
parent = s
s = ss
# check if se have reached the starting point
if len(stack) == 0:
return list(anticipating_nodes)
def has_cycle(self):
stack = []
visited = []
s = next(iter(self.graph))
stack.append(s)
visited.append(s)
parent = -2
indirect_parents = []
ss = s
on_the_way_back = False
anticipating_nodes = set()
while True:
# check if there is any non isolated nodes
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
and not on_the_way_back
):
len_stack_minus_one = len(stack) - 1
while len_stack_minus_one >= 0:
if stack[len_stack_minus_one] == node[1]:
anticipating_nodes.add(node[1])
break
else:
return True
if visited.count(node[1]) < 1:
stack.append(node[1])
visited.append(node[1])
ss = node[1]
break
# check if all the children are visited
if s == ss:
stack.pop()
on_the_way_back = True
if len(stack) != 0:
s = stack[len(stack) - 1]
else:
on_the_way_back = False
indirect_parents.append(parent)
parent = s
s = ss
# check if se have reached the starting point
if len(stack) == 0:
return False
def all_nodes(self):
return list(self.graph) return list(self.graph)
def dfs_time(self, s=-2, e=-1): # Additional methods would go here with doctests...
begin = time()
self.dfs(s, e)
end = time()
return end - begin
def bfs_time(self, s=-2): if __name__ == "__main__":
begin = time() import doctest
self.bfs(s) doctest.testmod()
end = time()
return end - begin