Skip to content

Commit a83f1fd

Browse files
authored
Merge pull request #14 from rlishtaba/feature/graph-algos
Feature/graph algos
2 parents c93cdff + 0d62fa5 commit a83f1fd

File tree

12 files changed

+275
-16
lines changed

12 files changed

+275
-16
lines changed

README.md

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Library of Algorithms, Data Structures, variety of solutions to common CS proble
1010

1111
Add this line to your application as managed dependency:
1212

13-
```python
13+
```bash
1414
py-algorithms>=0,<1
1515
```
1616

@@ -22,7 +22,7 @@ $ pip install py-algorithms
2222

2323
## What's inside?
2424

25-
^ Data Structures
25+
### Data Structures
2626
- Deque
2727
- Stack (LIFO)
2828
- Queue (FIFO)
@@ -33,44 +33,59 @@ $ pip install py-algorithms
3333
- Min PQ
3434
- Max PQ
3535
- Suffix Array
36+
3637
---
3738

38-
^ Search
39+
### Search
3940
- Binary Search
41+
4042
---
4143

42-
^ Sort
44+
### Sort
4345
- Quick Sort
4446
- Shell Sort (Shell method)
4547
- Heap Sort (Fibonacci heap)
4648
- Merge Sort
4749
- Comb Sort
4850
- Bubble Sort
4951
- Selection Sort
52+
5053
---
5154

52-
^ String algorithms
55+
### Graph Algorithms
56+
- Depth-First-Search (DFS)
57+
- Breadth-First-Search (BFS)
58+
- Topologial sort
59+
- Floyd–Warshall algorithm
60+
61+
---
62+
63+
### String Algorithms
5364
- Patttern Matching
5465
- Boyer–Moore string search
5566
- Knut-Morris-Prat string search
56-
---
67+
68+
---
5769

58-
^ Primality Tests
70+
### Primality Tests
5971
- Miller-Rabin primality test
6072
- Simple primality test
73+
6174
---
62-
63-
^ Algorithms
75+
76+
### Algorithms
6477
- Quick Union
6578
- Union Find
6679
- Weighted Union Find
6780
- Weighted Union Find with Path Compression
81+
6882
---
6983

70-
^ Challenges
84+
### Challenges
7185
- TopCoder (www.topcoder.com)
7286
- HackerRank problems & submissions (https://www.hackerrank.com)
7387
- CoderByte challenges (https://coderbyte.com)
88+
7489
---
7590

7691
### Challenges
@@ -472,11 +487,44 @@ ds.is_sub_str('blah') #=> False
472487

473488
---
474489

475-
---
490+
### Graph Algorithms
476491

477-
### String Algorithms
478492

493+
#### Topological sort
494+
495+
In the field of computer science, a topological sort or topological ordering of a directed graph is a
496+
linear ordering of its vertices such that for every directed edge uv from vertex u to vertex v, u comes before v in the ordering.
497+
For instance, the vertices of the graph may represent tasks to be performed, and the edges may represent
498+
constraints that one task must be performed before another; in this application, a topological ordering is just a valid sequence for the tasks.
499+
A topological ordering is possible if and only if the graph has no directed cycles, that is, if it is a directed acyclic graph (DAG).
500+
Any DAG has at least one topological ordering, and algorithms are known for constructing a topological ordering of any DAG in linear time.
479501

502+
![topo-sort](http://www.stoimen.com/blog/wp-content/uploads/2012/10/2.-Topological-Sort.png)
503+
504+
```python
505+
506+
from py_algorithms.graph import new_directed_graph, topological_sort_f1
507+
508+
509+
dag = new_directed_graph()
510+
a = dag.insert_vertex('A')
511+
b = dag.insert_vertex('B')
512+
c = dag.insert_vertex('C')
513+
d = dag.insert_vertex('D')
514+
e = dag.insert_vertex('E')
515+
516+
dag.insert_edge(a, e, None)
517+
dag.insert_edge(b, d, None)
518+
dag.insert_edge(c, d, None)
519+
dag.insert_edge(d, e, None)
520+
521+
topological_sort_f1(dag) #=> [#<Vertex(C)>, #<Vertex(B)>, #<Vertex(D)>, #<Vertex(A)>, #<Vertex(E)>]
522+
523+
```
524+
525+
---
526+
527+
### String Algorithms
480528

481529
![boyer-moore-media](https://www.researchgate.net/profile/Monther_Aldwairi/publication/237067573/figure/fig1/AS:299315785420823@1448373852591/Figure-1-The-Boyer-Moore-algorithm-illustrated-while-checking-a-signature-in-Snort.png)
482530

py_algorithms/graph/__init__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
__all__ = [
2+
'Graph',
3+
'Vertex',
4+
'directed_graph'
5+
'new_undirected_graph'
6+
'new_topological_sort_f1'
7+
]
8+
9+
from typing import List, Callable
10+
11+
from .adt.graph import Graph
12+
from .adt.vertex import Vertex
13+
from .topological_sort import TopologicalSort
14+
15+
16+
def new_directed_graph() -> Graph:
17+
return Graph(directed=True)
18+
19+
20+
def new_undirected_graph() -> Graph:
21+
return Graph(directed=False)
22+
23+
24+
def new_topological_sort() -> Callable[[Graph], List[Vertex]]:
25+
return TopologicalSort()
26+
27+
28+
topological_sort_f1 = new_topological_sort()

py_algorithms/graph/adt/__init__.py

Whitespace-only changes.

py_algorithms/graph/adt/edge.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from typing import Any
2+
from typing import Tuple
3+
from typing import Union
4+
5+
from .vertex import Vertex
6+
7+
8+
class Edge:
9+
__slots__ = '_origin', '_destination', '_element'
10+
11+
def __init__(self, origin: Vertex, destination: Vertex, element: Any):
12+
self._element = element
13+
self._destination = destination
14+
self._origin = origin
15+
16+
def endpoints(self) -> Tuple[Any, Any]:
17+
return self._origin, self._destination
18+
19+
def opposite(self, v) -> Union[None, Vertex]:
20+
return self._destination if v is self._origin else self._origin
21+
22+
def element(self) -> Any:
23+
return self._element
24+
25+
def __hash__(self):
26+
return hash((self._origin, self._destination,))
27+
28+
def __repr__(self):
29+
return "#<{}({}) ({},{})>" \
30+
.format(self.__class__.__name__,
31+
self.element(),
32+
self._origin.element(),
33+
self._destination.element())

py_algorithms/graph/adt/graph.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from typing import Any, Iterator
2+
from typing import List
3+
from typing import Set
4+
from typing import Union
5+
6+
from .edge import Edge
7+
from .vertex import Vertex
8+
9+
10+
class Graph:
11+
def __init__(self, directed: bool = False):
12+
self._outgoing = {}
13+
self._incoming = {} if directed else self._outgoing
14+
15+
def is_directed(self) -> bool:
16+
return self._incoming is not self._outgoing
17+
18+
def vertex_count(self) -> int:
19+
return len(self._outgoing)
20+
21+
def vertices(self) -> List[Vertex]:
22+
return list(self._outgoing.keys())
23+
24+
def edge_count(self) -> int:
25+
total = sum(len(self._outgoing[v]) for v in self._outgoing)
26+
return total if self.is_directed() else total // 2
27+
28+
def edges(self) -> Set[Edge]:
29+
result = set()
30+
for sub_map in self._outgoing.values():
31+
result.update(sub_map.values())
32+
return result
33+
34+
def get_edge(self, u, v) -> Union[None, Edge]:
35+
return self._outgoing[u].get(v)
36+
37+
def degree(self, v: Vertex, outgoing: bool = True) -> int:
38+
adj = self._outgoing if outgoing else self._incoming
39+
return len(adj[v])
40+
41+
def incident_edges(self,
42+
v: Vertex,
43+
outgoing: bool = True) -> Iterator[Edge]:
44+
adj = self._outgoing if outgoing else self._incoming
45+
for edge in adj[v].values():
46+
yield edge
47+
48+
def insert_vertex(self, element: Any = None) -> Vertex:
49+
v = Vertex(element)
50+
self._outgoing[v] = {}
51+
if self.is_directed():
52+
self._incoming[v] = {}
53+
return v
54+
55+
def insert_edge(self, origin: Vertex, destination: Vertex,
56+
element: Any) -> Edge:
57+
e = Edge(origin, destination, element)
58+
self._outgoing[origin][destination] = e
59+
self._incoming[destination][origin] = e
60+
return e

py_algorithms/graph/adt/vertex.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from typing import Any
2+
3+
4+
class Vertex:
5+
__slots__ = '_element'
6+
7+
def __init__(self, element: Any):
8+
self._element = element
9+
10+
def element(self) -> Any:
11+
return self._element
12+
13+
def __hash__(self) -> int:
14+
return hash(id(self))
15+
16+
def __repr__(self):
17+
return "#<{}({})>" \
18+
.format(self.__class__.__name__, self.element())
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from typing import List
2+
3+
from .adt.graph import Graph
4+
5+
from .adt.vertex import Vertex
6+
7+
8+
class TopologicalSort:
9+
"""
10+
Representing topological sort for a given DAG ADT
11+
"""
12+
13+
def __call__(self, graph: Graph) -> List[Vertex]:
14+
degrees = {}
15+
queue = []
16+
in_order = []
17+
18+
for u in graph.vertices():
19+
degrees[u] = graph.degree(u, outgoing=False)
20+
if degrees[u] == 0:
21+
queue.append(u)
22+
23+
while len(queue) > 0:
24+
u = queue.pop()
25+
in_order.append(u)
26+
for edge in graph.incident_edges(u):
27+
v = edge.opposite(u)
28+
degrees[v] -= 1
29+
if degrees[v] == 0:
30+
queue.append(v)
31+
32+
return in_order

setup.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,17 @@
3636
'hackerrank challenges', 'boyer-moore',
3737
'boyer-moore-string-search', 'primality-tes',
3838
'miller-rabin', 'miller-rabin-primality-test',
39-
'simple-primality-test'],
40-
description="Library of Algorithms, Data Structures, variety of solutions to common "
39+
'simple-primality-test', 'topological-sort', 'directed-graph',
40+
'DAG', 'directed-acyclic-graph', 'simple-graph',
41+
'undirected-graph', 'depth-first-search', 'DFS',
42+
'breadth-first-search', 'BFS'],
43+
description="Library of Algorithms, Data Structures, "
44+
"variety of solutions to common "
4145
"CS problems.",
42-
long_description="Library of Algorithms, Data Structures, variety of solutions to common "
43-
"CS problems. Algorithms and Data Structures implemented using pure awesome "
46+
long_description="Library of Algorithms, Data Structures, "
47+
"variety of solutions to common "
48+
"CS problems. Algorithms and Data Structures "
49+
"implemented using pure awesome "
4450
"Python.",
4551
license="MIT",
4652
url='https://github.com/rlishtaba/py-algorithms',

tests/py_algorithms/graph/__init__.py

Whitespace-only changes.

tests/py_algorithms/graph/adt/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)