Skip to content

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Algorithms Practice

Tests

This is a learning and portfolio repository. It contains Python implementations of algorithms and data structures that I use to practice algorithmic thinking, testing, complexity analysis, and project organization.

Goals

  • Implement common algorithms and data structures from first principles, rather than relying on built-in equivalents where the point is to practice the underlying technique.
  • Write tests that cover edge cases (empty input, duplicates, cycles, unreachable nodes) and not just the typical case.
  • Document the time and space complexity of each implementation based on what the code actually does.
  • Keep the project structured the way a small, readable Python package should be structured.

Project structure

algorithms-practice/
├── algorithms/
│   ├── searching/
│   │   └── binary_search.py
│   ├── sorting/
│   │   ├── quicksort.py
│   │   └── merge_sort.py
│   ├── graphs/
│   │   ├── traversal.py
│   │   └── shortest_path.py
│   ├── data_structures/
│   │   └── hash_table.py
│   └── dynamic_programming/
│       └── longest_common_subsequence.py
├── tests/
│   ├── searching/
│   ├── sorting/
│   ├── graphs/
│   ├── data_structures/
│   └── dynamic_programming/
├── .github/workflows/tests.yml
├── requirements.txt
└── README.md

Each implementation module lives under algorithms/<category>/ and has a matching test file under tests/<category>/.

Setup

Requires Python 3.11+.

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Running the tests

pytest

All 58 tests pass as of the current version of this repository.

Implementations

Implementation Category Time complexity Auxiliary space
binary_search Searching O(1) best, O(log n) average and worst O(1)
quicksort Sorting O(n log n) average, O(n²) worst O(n) average, O(n²) worst
merge_sort Sorting O(n log n) O(n)
breadth_first_search Graphs O(V + E) O(V)
depth_first_search Graphs O(V + E) O(V)
dijkstra Graphs O((V + E) log V) O(V + E)
HashTable Data structures O(1) average, O(n) worst O(n)
longest_common_subsequence Dynamic programming O(n · m) O(n · m)

Complexity notes are also documented in each module's docstring, next to the implementation they describe.

Implementation notes

  • Sorting returns new lists. Both quicksort and merge_sort build and return new lists rather than sorting in place, which is why their auxiliary space is O(n) rather than O(log n) or O(1).
  • Hash table uses separate chaining. Each bucket holds a Python list of (key, value) pairs. hash() is used only to pick a bucket index; collisions are resolved by scanning the bucket, not by wrapping a dictionary. The table doubles its bucket count whenever the load factor (entries / buckets) would exceed 0.75.
  • Dijkstra's algorithm rejects negative edge weights. Once a vertex is popped from the priority queue, this implementation assumes its shortest distance is final. That assumption breaks with negative weights, so dijkstra raises ValueError if it finds one instead of silently returning an incorrect result.
  • Graph traversals require every vertex to be a key, including vertices with no outgoing edges, so isolated and disconnected vertices are handled the same way as connected ones. Both breadth_first_search and depth_first_search raise KeyError if the given start vertex isn't in the graph.

What I practiced

  • Algorithm analysis — working out best/average/worst-case time complexity and auxiliary space complexity by reasoning about what each implementation actually does, not by assuming a textbook bound applies.
  • Testing and edge cases — writing tests for empty input, single elements, duplicates, cycles, disconnected components, and invalid input (negative weights, missing start vertices), not just the typical case.
  • Data structures — implementing a hash table from scratch, including collision handling with separate chaining and resizing based on load factor.
  • Graph algorithms — adjacency-list representations, traversal order, cycle handling, and shortest-path computation with a priority queue.
  • Dynamic programming — building a DP table bottom-up and reconstructing an actual solution (the subsequence itself) by walking the table backward, rather than only computing a length.

About

Algorithmic & data structure work

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages