Skip to content

Commit 023c5bc

Browse files
docs: add MIT license, contributing guide, and improve README
- Add LICENSE (MIT) and CONTRIBUTING.md with development workflow - Rewrite README with table of contents, complexity tables per operation, feature highlights, and dedicated testing section - Add license field to pyproject.toml
1 parent ae3b1a3 commit 023c5bc

4 files changed

Lines changed: 195 additions & 103 deletions

File tree

CONTRIBUTING.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Contributing
2+
3+
Contributions are welcome! Here's how to get started.
4+
5+
## Setup
6+
7+
```bash
8+
git clone https://github.com/azizjon-aliev/python-dsa.git
9+
cd python-dsa
10+
uv sync --group dev
11+
uv run pre-commit install
12+
```
13+
14+
## Development workflow
15+
16+
1. Create a branch from `main`
17+
2. Implement your changes
18+
3. Run checks before committing:
19+
20+
```bash
21+
uv run pytest tests/ -v # tests + coverage
22+
uv run ruff check . # linter
23+
uv run ruff format . # formatter
24+
uv run ty check # type checker
25+
```
26+
27+
4. Open a pull request
28+
29+
## Adding a new data structure
30+
31+
1. Create an ABC interface in `<module>/base.py`
32+
2. Implement the concrete class in its own file
33+
3. Add `__repr__`, `__str__`, `__eq__`, `__bool__`, `__len__`, `__contains__`
34+
4. Add tests in `tests/test_<module>.py`
35+
5. Add property-based tests in `tests/test_properties.py`
36+
6. Update `README.md` with usage examples and complexity table
37+
38+
## Adding a new algorithm
39+
40+
1. Create the module under `algorithms/<category>/`
41+
2. Add tests in `tests/test_<algorithm>.py`
42+
3. Update `README.md`
43+
44+
## Code style
45+
46+
- Follow existing patterns in the codebase
47+
- Use type hints everywhere
48+
- Add docstrings with complexity notes (e.g. `O(n)`)
49+
- Keep line length under 88 characters (enforced by ruff)
50+
- All pre-commit hooks must pass before merging

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Azizjon Aliev
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 123 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -3,93 +3,113 @@
33
![CI](https://github.com/azizjon-aliev/python-dsa/actions/workflows/ci.yml/badge.svg)
44
![Coverage](https://img.shields.io/badge/coverage-97%25-brightgreen)
55
![Python](https://img.shields.io/badge/python-3.13%2B-blue)
6+
![License](https://img.shields.io/badge/license-MIT-green)
67

7-
A collection of classic data structures and algorithms implemented in Python.
8+
Classic data structures and algorithms implemented from scratch in pure Python for learning purposes. Every implementation includes full type hints, ABC interfaces, `__repr__`/`__str__`/`__eq__`/`__bool__` support, and comprehensive tests.
89

9-
## Data Structures
10+
## Table of Contents
1011

11-
### Stacks
12+
- [Data Structures](#data-structures)
13+
- [Arrays](#arrays)
14+
- [Linked Lists](#linked-lists)
15+
- [Stacks](#stacks)
16+
- [Algorithms](#algorithms)
17+
- [Searching](#searching)
18+
- [Getting Started](#getting-started)
19+
- [Testing](#testing)
20+
- [Project Structure](#project-structure)
21+
- [Contributing](#contributing)
22+
- [License](#license)
1223

13-
| Implementation | Description |
14-
|---|---|
15-
| `ArrayStack` | Stack backed by a `DynamicArray`. O(1) amortised push/pop/peek. |
16-
| `LinkedListStack` | Stack backed by a `SinglyLinkedList`. O(1) worst-case push/pop/peek. |
17-
| `MinStack` | Extends `ArrayStack` with O(1) `get_min()` tracking via an auxiliary stack. |
24+
## Data Structures
1825

19-
```python
20-
from data_structures.stack.array_stack import ArrayStack
21-
from data_structures.stack.linked_list_stack import LinkedListStack
22-
from data_structures.stack.min_stack import MinStack
26+
All structures implement common Python protocols: `len()`, `in`, `bool()`, `==`, `repr()`, `str()`, iteration.
2327

24-
s = ArrayStack() # or LinkedListStack()
25-
s.push(1)
26-
s.push(2)
27-
s.peek() # 2
28-
s.pop() # 2
29-
s.size() # 1
30-
repr(s) # "ArrayStack([1])"
31-
str(s) # "1"
28+
### Arrays
3229

33-
ms = MinStack()
34-
ms.push(5)
35-
ms.push(3)
36-
ms.push(7)
37-
ms.get_min() # 3
38-
```
30+
| Implementation | Description | Access | Append | Insert/Remove |
31+
|---|---|---|---|---|
32+
| `StaticArray` | Fixed-capacity array | O(1) | O(1) | O(n) |
33+
| `DynamicArray` | Auto-resizing array (2x growth, 1/4 shrink) | O(1) | O(1)* | O(n) |
3934

40-
### Arrays
35+
\* amortised
4136

42-
| Implementation | Description |
43-
|---|---|
44-
| `StaticArray` | Fixed-capacity array. O(1) access, O(n) insert/remove. Raises `OverflowError` when full. |
45-
| `DynamicArray` | Resizable array that doubles on overflow and halves when sparse. O(1) amortised append/pop. |
37+
**Features:** negative indexing, slicing (`arr[1:3]`, `arr[::-1]`), construction from iterables.
4638

4739
```python
4840
from data_structures.array.static_array import StaticArray
4941
from data_structures.array.dynamic_array import DynamicArray
5042

51-
sa = StaticArray([1, 2, 3]) # or StaticArray(3) for empty
52-
repr(sa) # "StaticArray([1, 2, 3])"
53-
str(sa) # "[1, 2, 3]"
54-
list(sa) # [1, 2, 3]
55-
sa.pop() # 3
56-
sa.insert(0, 0)
57-
sa.remove(1) # 1
43+
# Static array — fixed capacity
44+
sa = StaticArray([1, 2, 3])
45+
sa[0] # 1
46+
sa[-1] # 3
47+
sa[::2] # [1, 3]
5848

59-
da = DynamicArray([1, 2, 3]) # or DynamicArray() for empty
60-
for i in range(4, 11):
49+
# Dynamic array — grows and shrinks automatically
50+
da = DynamicArray()
51+
for i in range(1, 6):
6152
da.append(i)
62-
len(da) # 10
63-
da.pop() # 10
64-
da.capacity # 16
53+
str(da) # "[1, 2, 3, 4, 5]"
54+
da[::-1] # [5, 4, 3, 2, 1]
55+
da == StaticArray([1, 2, 3, 4, 5]) # True (cross-type equality)
6556
```
6657

6758
### Linked Lists
6859

69-
| Implementation | Description |
70-
|---|---|
71-
| `SinglyLinkedList` | Singly linked list with O(1) push/pop at the head. |
72-
| `DoublyLinkedList` | Doubly linked list with O(1) push/pop at both head and tail. |
60+
| Implementation | Description | Push/Pop Front | Push/Pop Back |
61+
|---|---|---|---|
62+
| `SinglyLinkedList` | Forward-only traversal | O(1) ||
63+
| `DoublyLinkedList` | Bidirectional traversal | O(1) | O(1) |
64+
65+
**Features:** `reversed()` for DoublyLinkedList, cross-type equality, arrow-notation `str()`.
7366

7467
```python
7568
from data_structures.linked_list import SinglyLinkedList, DoublyLinkedList
7669

7770
sll = SinglyLinkedList()
7871
sll.push_front(1)
7972
sll.push_front(2)
80-
sll.peek_front() # 2
81-
str(sll) # "2 -> 1"
82-
sll.pop_front() # 2
83-
list(sll) # [1]
73+
str(sll) # "2 -> 1"
8474

8575
dll = DoublyLinkedList()
86-
dll.push_front(1)
76+
dll.push_back(1)
8777
dll.push_back(2)
8878
dll.push_back(3)
89-
str(dll) # "1 <-> 2 <-> 3"
90-
dll.peek_back() # 3
91-
dll.pop_back() # 3
92-
list(dll) # [1, 2]
79+
str(dll) # "1 <-> 2 <-> 3"
80+
list(reversed(dll)) # [3, 2, 1]
81+
82+
sll == dll # True (same elements in same order)
83+
```
84+
85+
### Stacks
86+
87+
| Implementation | Description | Push | Pop | Peek |
88+
|---|---|---|---|---|
89+
| `ArrayStack` | Backed by `DynamicArray` | O(1)* | O(1)* | O(1) |
90+
| `LinkedListStack` | Backed by `SinglyLinkedList` | O(1) | O(1) | O(1) |
91+
| `MinStack` | `ArrayStack` + O(1) `get_min()` | O(1)* | O(1)* | O(1) |
92+
93+
\* amortised
94+
95+
**Features:** cross-type equality, top-to-bottom `str()` display.
96+
97+
```python
98+
from data_structures.stack.array_stack import ArrayStack
99+
from data_structures.stack.linked_list_stack import LinkedListStack
100+
from data_structures.stack.min_stack import MinStack
101+
102+
s = ArrayStack()
103+
s.push(1); s.push(2); s.push(3)
104+
str(s) # "3 -> 2 -> 1"
105+
s.peek() # 3
106+
s.pop() # 3
107+
bool(s) # True
108+
109+
ms = MinStack()
110+
for v in [5, 3, 7, 1]:
111+
ms.push(v)
112+
ms.get_min() # 1
93113
```
94114

95115
## Algorithms
@@ -98,9 +118,11 @@ list(dll) # [1, 2]
98118

99119
| Algorithm | Time | Space | Description |
100120
|---|---|---|---|
101-
| `binary_search` | O(log n) | O(1) | Find element index in sorted sequence, or -1. |
102-
| `lower_bound` | O(log n) | O(1) | Index of first element >= target. |
103-
| `upper_bound` | O(log n) | O(1) | Index of first element > target. |
121+
| `binary_search` | O(log n) | O(1) | Index of target in sorted sequence, or -1 |
122+
| `lower_bound` | O(log n) | O(1) | Index of first element >= target |
123+
| `upper_bound` | O(log n) | O(1) | Index of first element > target |
124+
125+
Works with any indexable collection (`list`, `StaticArray`, `DynamicArray`).
104126

105127
```python
106128
from algorithms.searching.binary_search import binary_search, lower_bound, upper_bound
@@ -110,73 +132,71 @@ binary_search([1, 3, 5, 7, 9], 4) # -1
110132

111133
lower_bound([1, 3, 3, 3, 5], 3) # 1 (first >= 3)
112134
upper_bound([1, 3, 3, 3, 5], 3) # 4 (first > 3)
113-
114-
# Works with StaticArray and DynamicArray too
115-
binary_search(StaticArray([1, 2, 3]), 2) # 1
116-
binary_search(DynamicArray([1, 2, 3]), 2) # 1
117135
```
118136

119-
## Requirements
120-
121-
- Python >= 3.13
122-
- [uv](https://docs.astral.sh/uv/) (package manager)
123-
- [ruff](https://docs.astral.sh/ruff/) (linter & formatter)
124-
- [ty](https://docs.astral.sh/ty/) (type checker)
125-
- [pre-commit](https://pre-commit.com/) (git hooks)
126-
127137
## Getting Started
128138

139+
**Requirements:** Python >= 3.13, [uv](https://docs.astral.sh/uv/)
140+
129141
```bash
130-
# Install dependencies
142+
git clone https://github.com/azizjon-aliev/python-dsa.git
143+
cd python-dsa
131144
uv sync --group dev
145+
uv run pre-commit install
146+
```
147+
148+
## Testing
132149

133-
# Run tests (with coverage)
150+
```bash
151+
# Unit tests with coverage (248 tests, 97% coverage)
134152
uv run pytest tests/ -v
135153

136-
# Run benchmarks
154+
# Property-based tests (hypothesis)
155+
uv run pytest tests/test_properties.py -v
156+
157+
# Benchmarks
137158
uv run pytest tests/test_benchmarks.py --benchmark-enable --no-cov
138159

139-
# Run doctests
160+
# Doctests
140161
uv run pytest --doctest-modules data_structures/ algorithms/ --no-cov
141162

142-
# Lint & format
143-
uv run ruff check .
144-
uv run ruff format .
145-
146-
# Type check
147-
uv run ty check
148-
149-
# Run all pre-commit hooks
150-
uv run pre-commit run --all-files
163+
# Lint, format, type check
164+
uv run ruff check . && uv run ruff format --check . && uv run ty check
151165
```
152166

153167
## Project Structure
154168

155169
```
156170
data_structures/
157171
array/
158-
base.py # IArray ABC
159-
static_array.py # StaticArray implementation
160-
dynamic_array.py # DynamicArray implementation
172+
base.py # IArray ABC
173+
static_array.py # StaticArray
174+
dynamic_array.py # DynamicArray
161175
stack/
162-
base.py # IStack ABC and StackEmptyError
163-
array_stack.py # ArrayStack implementation
164-
linked_list_stack.py # LinkedListStack implementation
165-
min_stack.py # MinStack implementation
176+
base.py # IStack ABC, StackEmptyError
177+
array_stack.py # ArrayStack
178+
linked_list_stack.py # LinkedListStack
179+
min_stack.py # MinStack
166180
linked_list/
167-
base.py # ISinglyLinkedList & IDoublyLinkedList ABCs
168-
singly_linked_list.py # SinglyLinkedList
169-
doubly_linked_list.py # DoublyLinkedList
181+
base.py # ISinglyLinkedList, IDoublyLinkedList ABCs
182+
singly_linked_list.py
183+
doubly_linked_list.py
170184
algorithms/
171185
searching/
172-
binary_search.py # binary_search, lower_bound, upper_bound
186+
binary_search.py # binary_search, lower_bound, upper_bound
173187
tests/
174-
test_arrays.py # pytest suite for array implementations
175-
test_stacks.py # pytest suite for stack implementations
176-
test_linked_lists.py # pytest suite for linked list implementations
177-
test_binary_search.py # pytest suite for binary search
178-
test_benchmarks.py # pytest-benchmark performance tests
179-
test_properties.py # hypothesis property-based tests
180-
.pre-commit-config.yaml # pre-commit hooks (ruff, ty)
181-
pyproject.toml # project config, tools settings
188+
test_arrays.py # Array unit tests
189+
test_stacks.py # Stack unit tests
190+
test_linked_lists.py # Linked list unit tests
191+
test_binary_search.py # Binary search unit tests
192+
test_benchmarks.py # Performance benchmarks (pytest-benchmark)
193+
test_properties.py # Property-based tests (hypothesis)
182194
```
195+
196+
## Contributing
197+
198+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
199+
200+
## License
201+
202+
[MIT](LICENSE)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name = "python-dsa"
33
version = "0.1.0"
44
description = "Data Structures and Algorithms implemented in Python"
55
readme = "README.md"
6+
license = "MIT"
67
requires-python = ">=3.13"
78
dependencies = []
89

0 commit comments

Comments
 (0)