Commit e481175d authored by Bertrand Néron's avatar Bertrand Néron

add new basic non directed graph implementation

this second implementation implement graph with reachability list
parent e59f1e8c
import itertools
from typing import Iterator, Set, Sequence
class NDGraph:
"""
To handle Non Directed Graph
A graph is a collection of Nodes linked by edges
there are basically to way to implement a graph
- The graph handles Nodes, each nodes know it's neighbors
- The graph handles nodes and the edges between nodes
below I implemented the 2nd version.
"""
def __init__(self, nodes):
self.nodes = {n for n in nodes}
self.vertices = {n: set() for n in self.nodes}
def add_node(self, node: 'Node') -> None:
"""
Add a node to the graph
:param node: the node to add
:type node: :class:`Node`
:return: None
"""
self.nodes.add(node)
if node not in self.vertices:
self.vertices[node] = set()
def del_node(self, node: 'Node') -> None:
"""
Remove Node from Graph
:param node: the node to add
:type node: :class:`Node`
:return: None
"""
self.nodes.remove(node)
neighbors = self.vertices[node]
for nbh in neighbors:
self.vertices[nbh].remove(node)
del self.vertices[node]
def add_edge(self, node_1: 'Node', nodes: Sequence['Node']):
"""
Add vertice between node1 and all nodes in nodes
:param node_1: the reference node
:type node_1: :class:`Node`
:param nodes: the nodes connected to node_1
:type nodes: Sequence of :class:`Node`
:return: Node
"""
if node_1 not in self.nodes:
raise ValueError("the node_1 must be in Graph")
for n in nodes:
if n not in self.nodes:
raise ValueError("node must be add to Graph before creating edge")
self.vertices[node_1].add(n)
self.vertices[n].add(node_1)
def neighbors(self, node: 'Node') -> Set['Node']:
"""
return the nodes connected to node
:param node: the reference node
:type node: :class:`Node`
:return: a set of :class:`Node`
"""
return {n for n in self.vertices[node]}
class Node:
_id = itertools.count(0)
def __init__(self):
self.id: int = next(self._id)
def __hash__(self):
# to be usable in set an object must be hashable
# so we need to implement __hash__
# which must returm an int uniq per object
# here we ad the identifier of the Node
return self.id
def DFS(graph: NDGraph, node: Node) -> Iterator[Node]:
"""
**D**epth **F**irst **S**earch.
We start the path from the node given as argument,
This node is labeled as 'visited'
The neighbors of this node which have not been already 'visited' nor 'to visit' are labelled as 'to visit'
We pick the last element to visit and visit it
(The neighbors of this node which have not been already 'visited' nor 'to visit' are labelled as 'to visit').
on so on until there no nodes to visit anymore.
:param graph:
:param node:
:return:
"""
to_visit = [node]
visited = set()
while to_visit:
n = to_visit.pop(-1)
visited.add(n)
new_to_visit = graph.neighbors(n) - visited - set(to_visit)
to_visit.extend(new_to_visit)
yield n
def BFS(graph: NDGraph, node: Node) -> Iterator[Node]:
"""
**B**readth **F**irst **s**earch
We start the path from the node given as argument,
This node is labeled as 'visited'
The neighbors of this node which have not been already 'visited' nor 'to visit' are labelled as 'to visit'
we pick the **first** element of the nodes to visit and visit it.
(The neighbors of this node which have not been already 'visited' nor 'to visit' are labelled as 'to visit')
on so on until there no nodes to visit anymore.
:param graph:
:param node:
:return:
"""
to_visit = [node]
visited = set()
while to_visit:
n = to_visit.pop(0)
visited.add(n)
new_to_visit = graph.neighbors(n) - visited - set(to_visit)
to_visit.extend(new_to_visit)
yield n
from graph.graph_2 import NDGraph, Node, BFS, DFS
# We want to create a toy graph (not directed) to check our algos
# no
# / \
# n1 n3
# | |
# n2 n4
# \ /
# n5
nodes = [Node() for _ in range(6)]
g = NDGraph(nodes)
g.add_edge(nodes[0], (nodes[1], nodes[3]))
g.add_edge(nodes[1], (nodes[2],))
g.add_edge(nodes[2], (nodes[5],))
g.add_edge(nodes[3], (nodes[4],))
g.add_edge(nodes[4], (nodes[5],))
# The graph is created we will test
# a Depth First Search
# starting from n0
# the possible solutions are
# n0, n1,n2,n5,n4,n3
# n0, n3, n4, n5, n2, n1
print("DFS")
for n in DFS(g, nodes[0]):
print(n.id)
# a Breadth First Search
# starting n0
# the possible solutions are
# n0, n1, n3, n2, n4, n5
# n0, n3, n1, n2, n4, n5
# n0, n1, n3, n4, n2, n5
# ....
print("BFS")
for n in BFS(g, nodes[0]):
print(n.id)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment