Skip to content

Add Depth First Search and Graph structure #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions allalgorithms/searches/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .binary_search import *
from .fibonacci_search import *
from .jump_search import *
from .depth_first_search import *
30 changes: 30 additions & 0 deletions allalgorithms/searches/depth_first_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: UTF-8 -*-
#
# Depth first search works for a graph structure, comprised of adjacency lists.
# The All ▲lgorithms library for python
#
# Contributed by: Thomas Basche
# Github: @tombasche
#
from graph import Graph


def dfs(graph: Graph, source: str, target: str):
"""
Given a graph and a source node, find a target using Depth-first search

Great for traversing directed acyclic graphs, and thus job scheduling.
"""
visited = [source]
path = []
steps = 1
while visited:
node = visited.pop()
if node == target:
return target, path
if node in path:
continue
path.append(node)
for neighbour in graph.neighbours(node):
visited.append(neighbour)
steps += 1
1 change: 1 addition & 0 deletions allalgorithms/structures/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .graph import *
29 changes: 29 additions & 0 deletions allalgorithms/structures/graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
Simple graph class

Contributed by: Thomas Basche
GitHub: @tombasche

"""
from typing import Dict, List


class Graph:
"""
A graph represented by a dictionary of adjacency lists
"""
def __init__(self):
self.edges = {}

def neighbours(self, node: str) -> List:
return self.edges[node]

def __str__(self) -> Dict:
return self.edges

def __unicode__(self) -> Dict:
return self.edges

def __repr__(self) -> Dict:
return self.edges

44 changes: 44 additions & 0 deletions docs/searches/depth-first-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Depth first Search

Depth first search enables the finding of a specific node within a graph structure by traversing down the deepest paths first.
## Install

```
pip install allalgorithms
```

## Usage

```py
from allalgorithms.structures import Graph
from allalgorithms.searches import dfs

g = Graph()
g.edges = {
'A': ['B', 'C', 'G'],
'B': ['A', 'C', 'D'],
'C': ['A', 'B'],
'D': ['B', 'F', 'E'],
'E': ['D', 'F'],
'F': ['D', 'E'],
'G': ['A', 'H'],
'H': ['G'],
}

print(dfs(g, 'A', 'F))
# -> 'F', ['A', 'G', 'H', 'C', 'B', 'D', 'E']


```

## API

### dfs(array, source_node, query)

> Return node name if its found, otherwise returns `None`, as well as the path it took.

##### Params:

- `graph`: Graph object
- `source node`: Element to start search from
- `query`: Element to search for in the array
63 changes: 41 additions & 22 deletions tests/test_searches.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,50 @@
from allalgorithms.searches import (
binary_search,
fibonacci_search,
jump_search
dfs
)
from allalgorithms.structures import Graph


class TestSearches(unittest.TestCase):

def test_binary_search(self):
arr = [1, 2, 3, 7, 10, 19, 27, 77]
self.assertEqual(3, binary_search(arr, 7))
self.assertEqual(7, binary_search(arr, 77))
self.assertEqual(None, binary_search(arr, 8))
self.assertEqual(None, binary_search(arr, -1))

def test_fibonacci_search(self):
arr = [1, 2, 3, 7, 10, 19, 27, 77]
self.assertEqual(3, fibonacci_search(arr, 7))
self.assertEqual(7, fibonacci_search(arr, 77))
self.assertEqual(None, fibonacci_search(arr, 8))
self.assertEqual(None, fibonacci_search(arr, -1))

def test_jump_search(self):
arr = [1, 2, 3, 7, 10, 19, 27, 77]
self.assertEqual(3, binary_search(arr, 7))
self.assertEqual(7, binary_search(arr, 77))
self.assertEqual(None, binary_search(arr, 8))
self.assertEqual(None, binary_search(arr, -1))
def test_binary_search(self):
arr = [1, 2, 3, 7, 10, 19, 27, 77]
self.assertEqual(3, binary_search(arr, 7))
self.assertEqual(7, binary_search(arr, 77))
self.assertEqual(None, binary_search(arr, 8))
self.assertEqual(None, binary_search(arr, -1))

def test_fibonacci_search(self):
arr = [1, 2, 3, 7, 10, 19, 27, 77]
self.assertEqual(3, fibonacci_search(arr, 7))
self.assertEqual(7, fibonacci_search(arr, 77))
self.assertEqual(None, fibonacci_search(arr, 8))
self.assertEqual(None, fibonacci_search(arr, -1))

def test_jump_search(self):
arr = [1, 2, 3, 7, 10, 19, 27, 77]
self.assertEqual(3, binary_search(arr, 7))
self.assertEqual(7, binary_search(arr, 77))
self.assertEqual(None, binary_search(arr, 8))
self.assertEqual(None, binary_search(arr, -1))

def test_dfs_search(self):
g = Graph()
g.edges = {
'A': ['B', 'C', 'G'],
'B': ['A', 'C', 'D'],
'C': ['A', 'B'],
'D': ['B', 'F', 'E'],
'E': ['D', 'F'],
'F': ['D', 'E'],
'G': ['A', 'H'],
'H': ['G'],
}
result = dfs(g, 'A', 'F')
path = ['A', 'G', 'H', 'C', 'B', 'D', 'E']
self.assertEqual(('F', path), result)


if __name__ == '__main__':
unittest.main()
unittest.main()