Skip to content

Commit e4422cd

Browse files
committed
chore: move simple data-structures into data_structures.simple and remove ds_extra
1 parent 709c18e commit e4422cd

11 files changed

Lines changed: 427 additions & 0 deletions

File tree

data_structures/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""Top-level data_structures package.
2+
3+
This file exposes a small `simple` collection of educational data structures
4+
under `data_structures.simple` and provides a few convenience imports.
5+
"""
6+
7+
from . import simple
8+
9+
__all__ = ["simple"]

data_structures/simple/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Simple implementations of common data structures.
2+
3+
This subpackage contains small, educational implementations copied from the
4+
`ds_extra` package so they can be accessed via `data_structures.simple`.
5+
"""
6+
7+
from .linked_list import LinkedList
8+
from .stack import ListStack, LinkedStack
9+
from .queue import Queue
10+
from .bst import BinarySearchTree
11+
from .graph import Graph
12+
from .trie import Trie
13+
from .union_find import UnionFind
14+
from .min_heap import MinHeap
15+
16+
__all__ = [
17+
"LinkedList",
18+
"ListStack",
19+
"LinkedStack",
20+
"Queue",
21+
"BinarySearchTree",
22+
"Graph",
23+
"Trie",
24+
"UnionFind",
25+
"MinHeap",
26+
]

data_structures/simple/bst.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from typing import Any, Optional, List
2+
3+
4+
class BSTNode:
5+
def __init__(self, key: Any):
6+
self.key = key
7+
self.left: Optional[BSTNode] = None
8+
self.right: Optional[BSTNode] = None
9+
10+
11+
class BinarySearchTree:
12+
def __init__(self):
13+
self.root: Optional[BSTNode] = None
14+
15+
def insert(self, key: Any) -> None:
16+
def _insert(node: Optional[BSTNode], key: Any) -> BSTNode:
17+
if not node:
18+
return BSTNode(key)
19+
if key < node.key:
20+
node.left = _insert(node.left, key)
21+
else:
22+
node.right = _insert(node.right, key)
23+
return node
24+
25+
self.root = _insert(self.root, key)
26+
27+
def search(self, key: Any) -> bool:
28+
node = self.root
29+
while node:
30+
if key == node.key:
31+
return True
32+
node = node.left if key < node.key else node.right
33+
return False
34+
35+
def inorder(self) -> List[Any]:
36+
res: List[Any] = []
37+
38+
def _in(node: Optional[BSTNode]) -> None:
39+
if not node:
40+
return
41+
_in(node.left)
42+
res.append(node.key)
43+
_in(node.right)
44+
45+
_in(self.root)
46+
return res

data_structures/simple/graph.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from collections import defaultdict, deque
2+
from typing import Any, Dict, List
3+
4+
5+
class Graph:
6+
def __init__(self, directed: bool = False):
7+
self.adj: Dict[Any, List[Any]] = defaultdict(list)
8+
self.directed = directed
9+
10+
def add_edge(self, u: Any, v: Any) -> None:
11+
self.adj[u].append(v)
12+
if not self.directed:
13+
self.adj[v].append(u)
14+
15+
def bfs(self, start: Any) -> List[Any]:
16+
visited = set()
17+
order = []
18+
q = deque([start])
19+
visited.add(start)
20+
while q:
21+
u = q.popleft()
22+
order.append(u)
23+
for v in self.adj[u]:
24+
if v not in visited:
25+
visited.add(v)
26+
q.append(v)
27+
return order
28+
29+
def dfs(self, start: Any) -> List[Any]:
30+
visited = set()
31+
order = []
32+
33+
def _dfs(u: Any) -> None:
34+
visited.add(u)
35+
order.append(u)
36+
for v in self.adj[u]:
37+
if v not in visited:
38+
_dfs(v)
39+
40+
_dfs(start)
41+
return order
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from typing import Any, Optional
2+
3+
4+
class Node:
5+
def __init__(self, value: Any):
6+
self.value = value
7+
self.next: Optional[Node] = None
8+
9+
10+
class LinkedList:
11+
def __init__(self):
12+
self.head: Optional[Node] = None
13+
14+
def append(self, value: Any) -> None:
15+
node = Node(value)
16+
if not self.head:
17+
self.head = node
18+
return
19+
cur = self.head
20+
while cur.next:
21+
cur = cur.next
22+
cur.next = node
23+
24+
def prepend(self, value: Any) -> None:
25+
node = Node(value)
26+
node.next = self.head
27+
self.head = node
28+
29+
def find(self, value: Any) -> Optional[Node]:
30+
cur = self.head
31+
while cur:
32+
if cur.value == value:
33+
return cur
34+
cur = cur.next
35+
return None
36+
37+
def delete(self, value: Any) -> bool:
38+
cur = self.head
39+
prev = None
40+
while cur:
41+
if cur.value == value:
42+
if prev:
43+
prev.next = cur.next
44+
else:
45+
self.head = cur.next
46+
return True
47+
prev, cur = cur, cur.next
48+
return False
49+
50+
def to_list(self) -> list:
51+
out = []
52+
cur = self.head
53+
while cur:
54+
out.append(cur.value)
55+
cur = cur.next
56+
return out

data_structures/simple/min_heap.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import heapq
2+
from typing import Any, List, Optional
3+
4+
5+
class MinHeap:
6+
def __init__(self):
7+
self._data: List[Any] = []
8+
9+
def push(self, v: Any) -> None:
10+
heapq.heappush(self._data, v)
11+
12+
def pop(self) -> Any:
13+
return heapq.heappop(self._data)
14+
15+
def peek(self) -> Optional[Any]:
16+
return self._data[0] if self._data else None

data_structures/simple/queue.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from collections import deque
2+
from typing import Any, Optional
3+
4+
5+
class Queue:
6+
def __init__(self):
7+
self._dq = deque()
8+
9+
def enqueue(self, v: Any) -> None:
10+
self._dq.append(v)
11+
12+
def dequeue(self) -> Any:
13+
return self._dq.popleft()
14+
15+
def peek(self) -> Optional[Any]:
16+
return self._dq[0] if self._dq else None
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""Smoke test runner for the `data_structures.simple` package."""
2+
3+
import sys
4+
5+
from data_structures.simple.linked_list import LinkedList
6+
from data_structures.simple.stack import ListStack, LinkedStack
7+
from data_structures.simple.queue import Queue
8+
from data_structures.simple.bst import BinarySearchTree
9+
from data_structures.simple.graph import Graph
10+
from data_structures.simple.trie import Trie
11+
from data_structures.simple.union_find import UnionFind
12+
from data_structures.simple.min_heap import MinHeap
13+
14+
15+
def run() -> None:
16+
failures = []
17+
18+
try:
19+
ll = LinkedList()
20+
ll.append(1)
21+
ll.append(2)
22+
ll.prepend(0)
23+
assert ll.to_list() == [0, 1, 2]
24+
assert ll.find(1) is not None
25+
assert ll.delete(1) is True
26+
assert ll.to_list() == [0, 2]
27+
print("LinkedList: OK")
28+
except Exception as e:
29+
failures.append(('LinkedList', e))
30+
31+
try:
32+
s = ListStack()
33+
s.push(1)
34+
s.push(2)
35+
assert s.peek() == 2
36+
assert s.pop() == 2
37+
38+
ls = LinkedStack()
39+
ls.push('a')
40+
ls.push('b')
41+
assert ls.peek() == 'b'
42+
assert ls.pop() == 'b'
43+
print("Stacks: OK")
44+
except Exception as e:
45+
failures.append(('Stacks', e))
46+
47+
try:
48+
q = Queue()
49+
q.enqueue(1)
50+
q.enqueue(2)
51+
assert q.peek() == 1
52+
assert q.dequeue() == 1
53+
print("Queue: OK")
54+
except Exception as e:
55+
failures.append(('Queue', e))
56+
57+
try:
58+
bst = BinarySearchTree()
59+
for v in [3, 1, 4, 2]:
60+
bst.insert(v)
61+
assert bst.search(2)
62+
assert bst.inorder() == [1, 2, 3, 4]
63+
print("BST: OK")
64+
except Exception as e:
65+
failures.append(('BST', e))
66+
67+
try:
68+
g = Graph()
69+
g.add_edge(1, 2)
70+
g.add_edge(1, 3)
71+
order_bfs = g.bfs(1)
72+
assert 1 in order_bfs
73+
order_dfs = g.dfs(1)
74+
assert 1 in order_dfs
75+
print("Graph: OK")
76+
except Exception as e:
77+
failures.append(('Graph', e))
78+
79+
try:
80+
t = Trie()
81+
t.insert('hello')
82+
assert t.search('hello')
83+
assert t.starts_with('hel')
84+
print("Trie: OK")
85+
except Exception as e:
86+
failures.append(('Trie', e))
87+
88+
try:
89+
uf = UnionFind()
90+
for x in [1, 2, 3]:
91+
uf.make_set(x)
92+
uf.union(1, 2)
93+
assert uf.find(1) == uf.find(2)
94+
print("UnionFind: OK")
95+
except Exception as e:
96+
failures.append(('UnionFind', e))
97+
98+
try:
99+
h = MinHeap()
100+
h.push(5)
101+
h.push(1)
102+
h.push(3)
103+
assert h.peek() == 1
104+
assert h.pop() == 1
105+
print("MinHeap: OK")
106+
except Exception as e:
107+
failures.append(('MinHeap', e))
108+
109+
if failures:
110+
print('\nFAILURES:')
111+
for name, exc in failures:
112+
print(f"- {name}: {exc}")
113+
sys.exit(2)
114+
else:
115+
print('\nAll data_structures.simple smoke tests passed.')
116+
117+
118+
if __name__ == '__main__':
119+
run()

data_structures/simple/stack.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from typing import Any, Optional
2+
from .linked_list import LinkedList
3+
4+
5+
class ListStack:
6+
def __init__(self):
7+
self._data = []
8+
9+
def push(self, v: Any) -> None:
10+
self._data.append(v)
11+
12+
def pop(self) -> Any:
13+
return self._data.pop()
14+
15+
def peek(self) -> Optional[Any]:
16+
return self._data[-1] if self._data else None
17+
18+
19+
class LinkedStack:
20+
def __init__(self):
21+
self._list = LinkedList()
22+
23+
def push(self, v: Any) -> None:
24+
self._list.prepend(v)
25+
26+
def pop(self) -> Any:
27+
if not self._list.head:
28+
raise IndexError("pop from empty stack")
29+
val = self._list.head.value
30+
self._list.head = self._list.head.next
31+
return val
32+
33+
def peek(self) -> Optional[Any]:
34+
return self._list.head.value if self._list.head else None

0 commit comments

Comments
 (0)